first draft. initialization working

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@11231 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
areviu 2018-01-07 11:28:07 +00:00
parent 6b741c1eb4
commit 5a733a0d43
24 changed files with 8312 additions and 0 deletions

View File

@ -0,0 +1,499 @@
#include <string.h>
#include "hal.h"
#include "sama_sdmmc_lld.h"
#include "ch_sdmmc_device.h"
#include "ch_sdmmc_cmds.h"
#include "ch_sdmmc_sdio.h"
#include "ch_sdmmc_sd.h"
#include "ch_sdmmc_mmc.h"
/** SD/MMC transfer rate unit codes (10K) list */
const uint16_t sdmmcTransUnits[8] = {
10, 100, 1000, 10000,
0, 0, 0, 0
};
/** SD transfer multiplier factor codes (1/10) list */
const uint8_t sdTransMultipliers[16] = {
0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
};
uint32_t SdmmcGetMaxFreq(SdmmcDriver *drv)
{
uint32_t rate = 0;
sSdCard * pSd = &drv->card;
#ifndef SDMMC_TRIM_MMC
if ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmMMC) {
if (pSd->bSpeedMode == SDMMC_TIM_MMC_HS200)
rate = 200000ul;
else if (pSd->bSpeedMode == SDMMC_TIM_MMC_HS_DDR
|| (pSd->bSpeedMode == SDMMC_TIM_MMC_HS_SDR
&& MMC_EXT_CARD_TYPE(pSd->EXT) & 0x2))
rate = 52000ul;
else if (pSd->bSpeedMode == SDMMC_TIM_MMC_HS_SDR)
rate = 26000ul;
else
rate = SdmmcDecodeTransSpeed(SD_CSD_TRAN_SPEED(pSd->CSD),
sdmmcTransUnits, mmcTransMultipliers);
}
#endif
if ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD) {
rate = SdmmcDecodeTransSpeed(SD_CSD_TRAN_SPEED(pSd->CSD),
sdmmcTransUnits, sdTransMultipliers);
if (pSd->bSpeedMode == SDMMC_TIM_SD_SDR104 && rate == 200000ul)
rate = 208000ul;
else if (pSd->bSpeedMode == SDMMC_TIM_SD_DDR50)
rate /= 2ul;
}
return rate * 1000ul;
}
/**
* Update SD/MMC information.
* Update CSD for card speed switch.
* Update ExtDATA for any card function switch.
* \param pSd Pointer to a SD card driver instance.
* \return error code when update CSD error.
*/
void SdMmcUpdateInformation(SdmmcDriver *drv, bool csd, bool extData)
{
uint8_t error;
/* Update CSD for new TRAN_SPEED value */
if (csd) {
SdMmcSelect(drv, 0, 1);
/* Wait for 14 usec (or more) */
t_usleep(drv,20);
error = Cmd9(drv);
if (error)
return;
SdMmcSelect(drv, drv->card.wAddress, 1);
}
if (extData) {
if ((drv->card.bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD)
SdGetExtInformation(drv);
#ifndef SDMMC_TRIM_MMC
else if ((drv->card.bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmMMC)
MmcGetExtInformation(drv);
#endif
}
}
uint8_t SDMMC_Lib_SdStart(SdmmcDriver *drv, bool * retry)
{
uint64_t mem_size;
uint32_t freq, drv_err, status;
uint8_t error;
bool flag;
sSdCard *pSd = &drv->card;
*retry = false;
drv->card.bSpeedMode = drv->card.bCardSigLevel == 0 ? SDMMC_TIM_SD_SDR12 : SDMMC_TIM_SD_DS;
drv->timeout_elapsed = -1;
HwSetHsMode(drv, drv->card.bSpeedMode);
/* Consider switching to low signaling level, as a prerequisite to
* switching to any UHS-I timing mode */
if (drv->card.bCardSigLevel == 1) {
error = Cmd11(drv, &status);
if (error)
return error;
if ((status & STATUS_STATE) != STATUS_READY) {
TRACE_1("st %lx\n\r", status);
}
drv->card.bCardSigLevel = 0;
error = HwPowerDevice(drv, SDMMC_PWR_STD_VDD_LOW_IO);
if (error)
return error;
error = HwSetHsMode(drv, SDMMC_TIM_SD_SDR12);
if (error)
return error;
drv->card.bSpeedMode = SDMMC_TIM_SD_SDR12;
}
/* The host then issues the command ALL_SEND_CID (CMD2) to the card to get
* its unique card identification (CID) number.
* Card that is unidentified (i.e. which is in Ready State) sends its CID
* number as the response (on the CMD line). */
error = Cmd2(drv);
if (error)
return error;
/* Thereafter, the host issues CMD3 (SEND_RELATIVE_ADDR) asks the
* card to publish a new relative card address (RCA), which is shorter than
* CID and which is used to address the card in the future data transfer
* mode. Once the RCA is received the card state changes to the Stand-by
* State. At this point, if the host wants to assign another RCA number, it
* can ask the card to publish a new number by sending another CMD3 command
* to the card. The last published RCA is the actual RCA number of the
* card. */
error = Cmd3(drv);
if (error)
return error;
else {
TRACE_1("RCA=%u\n\r",drv->card.wAddress );
}
/* SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register),
* e.g. block length, card storage capacity, etc... */
error = Cmd9(drv);
if (error)
return error;
/* Now select the card, to TRAN state */
error = SdMmcSelect(drv, drv->card.wAddress , 0);
if (error)
return error;
/* Get extended information of the card */
SdMmcUpdateInformation(drv, true, true);
/* Enable more bus width Mode */
error = SdDecideBuswidth(drv);
if (error) {
//trace_error("Bus width %s\n\r", SD_StringifyRetCode(error));
return SDMMC_ERR;
}
/* Consider HS and UHS-I timing modes */
error = SdEnableHighSpeed(drv);
if (error) {
*retry = error == (SDMMC_STATE && (&drv->card.bCardSigLevel != 0));
return error;
}
/* Update card information since status changed */
flag = drv->card.bSpeedMode != SDMMC_TIM_SD_DS
&& drv->card.bSpeedMode != SDMMC_TIM_SD_SDR12;
if (flag || drv->card.bBusMode > 1)
SdMmcUpdateInformation(drv, flag, true);
/* Find out if the device supports the SET_BLOCK_COUNT command.
* SD devices advertise in SCR.CMD_SUPPORT whether or not they handle
* the SET_BLOCK_COUNT command. */
drv->card.bSetBlkCnt = SD_SCR_CMD23_SUPPORT(pSd->SCR);
/* Now, if the device does not support the SET_BLOCK_COUNT command, then
* the legacy STOP_TRANSMISSION command shall be issued, though not at
* the same timing. */
if (!drv->card.bSetBlkCnt) {
/* In case the driver does not automatically issue the
* STOP_TRANSMISSION command, we'll have to do it ourselves. */
drv->control_param = 0;
drv_err = sdmmc_device_control(drv, SDMMC_IOCTL_GET_XFERCOMPL);
if (drv_err != SDMMC_OK || !drv->control_param)
drv->card.bStopMultXfer = 1;
}
/* Ask the driver to implicitly send the SET_BLOCK_COUNT command,
* immediately before every READ_MULTIPLE_BLOCK and WRITE_MULTIPLE_BLOCK
* command. Or, if the current device does not support SET_BLOCK_COUNT,
* instruct the driver to stop using this command. */
drv->control_param = pSd->bSetBlkCnt;
drv_err = sdmmc_device_control(drv,SDMMC_IOCTL_SET_LENPREFIX);
/* In case the driver does not support this function, we'll take it in
* charge. */
if (drv->card.bSetBlkCnt && drv_err == SDMMC_OK && drv->control_param)
drv->card.bSetBlkCnt = 0;
/* In the case of a Standard Capacity SD Memory Card, this command sets the
* block length (in bytes) for all following block commands
* (read, write, lock).
* Default block length is fixed to 512 Bytes.
* Set length is valid for memory access commands only if partial block read
* operation are allowed in CSD.
* In the case of a High Capacity SD Memory Card, block length set by CMD16
* command does not affect the memory read and write commands. Always 512
* Bytes fixed block length is used. This command is effective for
* LOCK_UNLOCK command.
* In both cases, if block length is set larger than 512Bytes, the card sets
* the BLOCK_LEN_ERROR bit. */
if (drv->card.bCardType == CARD_SD) {
error = Cmd16(drv, SDMMC_BLOCK_SIZE);
if (error)
return error;
}
drv->card.wCurrBlockLen = SDMMC_BLOCK_SIZE;
if (SD_CSD_STRUCTURE(pSd->CSD) >= 1) {
drv->card.wBlockSize = 512;
mem_size = SD_CSD_BLOCKNR_HC(pSd->CSD);
drv->card.dwNbBlocks = mem_size >> 32 ? 0xFFFFFFFF : (uint32_t)mem_size;
if (drv->card.dwNbBlocks >= 0x800000)
drv->card.dwTotalSize = 0xFFFFFFFF;
else
drv->card.dwTotalSize = drv->card.dwNbBlocks * 512UL;
}
else {
drv->card.wBlockSize = 512;
mem_size = SD_CSD_TOTAL_SIZE(pSd);
drv->card.dwNbBlocks = (uint32_t)(mem_size >> 9);
drv->card.dwTotalSize = mem_size >> 32 ? 0xFFFFFFFF
: (uint32_t)mem_size;
}
/* Automatically select the max device clock frequency */
/* Calculate transfer speed */
freq = SdmmcGetMaxFreq(drv);
#ifndef SDMMC_TRIM_SDIO
if (drv->card.bCardType & CARD_TYPE_bmSDIO)
freq = min_u32(freq, SdioGetMaxFreq(drv));
#endif
error = HwSetClock(drv, &freq);
drv->card.dwCurrSpeed = freq;
if (error != SDMMC_OK && error != SDMMC_CHANGED) {
TRACE_1("clk %s\n\r", SD_StringifyRetCode(error));
return error;
}
/* Check device status and eat past exceptions, which would otherwise
* prevent upcoming data transaction routines from reliably checking
* fresh exceptions. */
error = Cmd13(drv, &status);
if (error)
return error;
status = status & ~STATUS_STATE & ~STATUS_READY_FOR_DATA & ~STATUS_APP_CMD;
//if (status)
// trace_warning("st %lx\n\r", status);
return SDMMC_OK;
}
/**
* \brief Run the SD/MMC/SDIO Mode initialization sequence.
* This function runs the initialization procedure and the identification
* process. Then it leaves the card in ready state. The following procedure must
* check the card type and continue to put the card into tran(for memory card)
* or cmd(for io card) state for data exchange.
* \param pSd Pointer to a SD card driver instance.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "SD_ERROR code".
*/
uint8_t SdMmcIdentify(SdmmcDriver *drv)
{
uint8_t error;
bool high_capacity;
uint8_t dev_type = CARD_UNKNOWN;
bool sd_v2 = false;
#ifndef SDMMC_TRIM_SDIO
/* Reset SDIO: CMD52, write 1 to RES bit in CCCR */
{
uint32_t status = SDIO_RES;
error = Cmd52(drv, 1, SDIO_CIA, 0, SDIO_IOA_REG, &status);
if ((error && error != SDMMC_NO_RESPONSE) || (!error && status & STATUS_SDIO_R5)) {
TRACE_2("IOrst %s st %lx\n\r",
SD_StringifyRetCode(error), status);
}
}
#endif
/* Reset MEM: CMD0 */
error = Cmd0(drv, 0);
t_msleep(drv,200);
if (error) {
TRACE_1("rst %s\n\r", SD_StringifyRetCode(error));
}
/* CMD8 is newly added in the Physical Layer Specification Version 2.00 to
* support multiple voltage ranges and used to check whether the card
* supports supplied voltage. The version 2.00 host shall issue CMD8 and
* verify voltage before card initialization.
* The host that does not support CMD8 shall supply high voltage range... */
error = SdCmd8(drv, SD_IFC_VHS_27_36 >> SD_IFC_VHS_Pos);
if (!error)
sd_v2 = true;
else if (error != SDMMC_NO_RESPONSE)
return SDMMC_ERR;
else {
/* No response to CMD8. Wait for 130 usec (or more). */
t_usleep(drv,200);
}
#ifndef SDMMC_TRIM_SDIO
/* CMD5 is newly added for SDIO initialize & power on */
{
uint32_t status = 0;
error = Cmd5(drv, &status);
if (!error && (status & SDIO_OCR_NF) > 0) {
//int8_t elapsed;
/* Card has SDIO function. Wait until it raises the
* IORDY flag, which may take up to 1s, i.e. 1000 system
* ticks. */
for (drv->timeout_elapsed = 0;
!(status & SD_OCR_BUSYN) && !error && !drv->timeout_elapsed; ) {
status &= SD_HOST_VOLTAGE_RANGE;
error = Cmd5(drv, &status);
}
if (!(status & SD_OCR_BUSYN) && !error)
error = SDMMC_BUSY;
if (error) {
TRACE_1("SDIO oc %s\n\r",SD_StringifyRetCode(error));
return SDMMC_ERR;
}
TRACE("SDIO\n\r");
dev_type = status & SDIO_OCR_MP ? CARD_SDCOMBO : CARD_SDIO;
}
}
#endif
if (dev_type != CARD_SDIO) {
/* The device should have memory (MMC or SD or COMBO).
* Try to initialize SD memory. */
bool low_sig_lvl = HwIsTimingSupported(drv, SDMMC_TIM_SD_SDR12);
high_capacity = sd_v2;
error = Acmd41(drv, &low_sig_lvl, &high_capacity);
if (!error) {
TRACE_1("SD%s MEM\n\r", high_capacity ? "HC" : "");
dev_type |= high_capacity ? CARD_SDHC : CARD_SD;
if (drv->card.bCardSigLevel == 2 && low_sig_lvl)
drv->card.bCardSigLevel = 1;
}
else if (dev_type == CARD_SDCOMBO)
dev_type = CARD_SDIO;
}
#ifndef SDMMC_TRIM_MMC
if (dev_type == CARD_UNKNOWN) {
/* Try MMC initialize */
uint8_t count;
for (error = SDMMC_NO_RESPONSE, count = 0;
error == SDMMC_NO_RESPONSE && count < 10;
count++)
error = Cmd0(drv, 0);
if (error) {
TRACE_1("MMC rst %s\n\r",
SD_StringifyRetCode(error));
return SDMMC_ERR;
}
high_capacity = false;
error = Cmd1(drv, &high_capacity);
if (error) {
TRACE_1("MMC oc %s\n\r",
SD_StringifyRetCode(error));
return SDMMC_ERR;
}
/* MMC card identification OK */
TRACE("MMC\n\r");
dev_type = high_capacity ? CARD_MMCHD : CARD_MMC;
}
#endif
if (dev_type == CARD_UNKNOWN) {
TRACE("Unknown card\n\r");
return SDMMC_ERR;
}
drv->card.bCardType = dev_type;
return 0;
}
/**
* Switch card state between STBY and TRAN (or CMD and TRAN)
* \param pSd Pointer to a SD card driver instance.
* \param address Card address to TRAN, 0 to STBY
* \param statCheck Whether to check the status before CMD7.
*/
uint8_t SdMmcSelect(SdmmcDriver *drv, uint16_t address, uint8_t statCheck)
{
uint8_t error;
uint32_t status, currState;
uint32_t targetState = address ? STATUS_TRAN : STATUS_STBY;
uint32_t srcState = address ? STATUS_STBY : STATUS_TRAN;
/* At this stage the Initialization and identification process is achieved
* The SD card is supposed to be in Stand-by State */
while (statCheck) {
error = Cmd13(drv, &status);
if (error)
return error;
if (status & STATUS_READY_FOR_DATA) {
currState = status & STATUS_STATE;
if (currState == targetState)
return 0;
if (currState != srcState) {
TRACE_1("st %lx\n\r", currState);
return SDMMC_ERR;
}
break;
}
}
/* Switch to Transfer state. Select the current SD/MMC
* so that SD ACMD6 can process or EXT_CSD can read. */
error = Cmd7(drv, address);
return error;
}
/**
* \brief Decode Trans Speed Value
* \param code The trans speed code value.
* \param unitCodes Unit list in 10K, 0 as unused value.
* \param multiCodes Multiplier list in 1/10, index 1 ~ 15 is valid.
*/
uint32_t SdmmcDecodeTransSpeed(uint32_t code,
const uint16_t * unitCodes, const uint8_t * multiCodes)
{
uint32_t speed;
uint8_t unitI, mulI;
/* Unit code is valid ? */
unitI = code & 0x7;
if (unitCodes[unitI] == 0)
return 0;
/* Multi code is valid ? */
mulI = (code >> 3) & 0xF;
if (multiCodes[mulI] == 0)
return 0;
speed = (uint32_t)unitCodes[unitI] * multiCodes[mulI];
return speed;
}

View File

@ -0,0 +1,179 @@
#ifndef _CH_SDMMC_LIB_H
#define _CH_SDMMC_LIB_H
#include "sama_sdmmc_conf.h"
/**
* \brief SD/MMC card driver structure.
* It holds the current command being processed and the SD/MMC card address.
*
* The first members of this structure may have to follow the DMA alignment
* requirements.
*/
typedef struct _SdCard {
uint8_t *EXT; /**< MMC Extended Device-Specific Data Register
* (EXT_CSD). This member may have to follow
* the DMA alignment requirements. */
uint8_t *SSR; /**< SD Status Register (SSR).
* This member may have to follow the DMA
* alignment requirements. */
uint8_t *SCR; /**< SD CARD Configuration Register (SCR).
* This member may have to follow the DMA
* alignment requirements. */
uint8_t *sandbox1; /**< Multi-purpose temporary buffer.
* This member may have to follow the DMA
* alignment requirements. */
uint8_t *sandbox2; /**< Multi-purpose temporary buffer.
* This member may have to follow the DMA
* alignment requirements. */
uint32_t CID[128 / 8 / 4]; /**< Card Identification (CID register) */
uint32_t CSD[128 / 8 / 4]; /**< Card-specific data (CSD register) */
void *pExt; /**< Pointer to extension data for SD/MMC/SDIO */
uint32_t dwTotalSize; /**< Card total size
(0xffffffff to see number of blocks */
uint32_t dwNbBlocks; /**< Card total number of blocks */
uint16_t wBlockSize; /**< Card block size reported */
uint16_t wCurrBlockLen; /**< Block length used */
uint32_t dwCurrSpeed; /**< Device clock frequency used, in Hz */
uint16_t wAddress; /**< Current card address */
uint8_t bCardType; /**< SD/MMC/SDIO card type \sa sdmmc_cardtype */
uint8_t bCardSigLevel; /**< 0/1/2 for low/ready_for_low/high signaling
* level used by the card, respectively. */
uint8_t bSpeedMode; /**< Timing mode */
uint8_t bBusMode; /**< 1/4/8 bit data bus mode */
uint8_t bStatus; /**< Unrecovered error */
uint8_t bSetBlkCnt; /**< Explicit SET_BLOCK_COUNT command used */
uint8_t bStopMultXfer; /**< Explicit STOP_TRANSMISSION command used */
} sSdCard;
/**
* Sdmmc command operation settings union.
*/
typedef union _SdmmcCmdOperation {
uint16_t wVal;
struct _SdmmcOpBm {
uint16_t powerON:1, /**< Do power on initialize */
sendCmd:1, /**< Send SD/MMC command */
xfrData:2, /**< Send/Stop data transfer */
respType:3, /**< Response type (1~7) */
crcON:1, /**< CRC is used (SPI) */
odON:1, /**< Open-Drain is ON (MMC) */
ioCmd:1, /**< SDIO command */
checkBsy:1; /**< Busy check is ON */
} bmBits;
} uSdmmcCmdOp;
/**
* Sdmmc command instance.
*/
typedef struct _SdmmcCommand {
/** Optional user-provided callback function. */
//fSdmmcCallback fCallback;
/** Optional argument to the callback function. */
void *pArg;
/** Data buffer. It shall follow the peripheral and DMA alignment
* requirements, which are peripheral and driver dependent. */
uint8_t *pData;
/** Size of data block in bytes. */
uint16_t wBlockSize;
/** Number of blocks to be transfered */
uint16_t wNbBlocks;
/** Response buffer. */
uint32_t *pResp;
/** Command argument. */
uint32_t dwArg;
/** Command operation settings */
uSdmmcCmdOp cmdOp;
/** Command index */
uint8_t bCmd;
/** Command return status */
uint8_t bStatus;
} sSdmmcCommand;
/** SD/MMC Return codes */
typedef enum {
SDMMC_OK = 0, /**< Operation OK */
SDMMC_LOCKED = 1, /**< Failed because driver locked */
SDMMC_BUSY = 2, /**< Failed because driver busy */
SDMMC_NO_RESPONSE = 3, /**< Failed because card not respond */
SDMMC_CHANGED, /**< Setting param changed due to limitation */
SDMMC_ERR, /**< Failed with general error */
SDMMC_ERR_IO, /**< Failed because of IO error */
SDMMC_ERR_RESP, /**< Error reported in response code */
SDMMC_NOT_INITIALIZED, /**< Fail to initialize */
SDMMC_PARAM, /**< Parameter error */
SDMMC_STATE, /**< State error */
SDMMC_USER_CANCEL, /**< Canceled by user */
SDMMC_NOT_SUPPORTED /**< Command(Operation) not supported */
} eSDMMC_RC;
/**
* \addtogroup sdmmc_cardtype SD/MMC Card Types
* Here lists the SD/MMC card types.
* - Card Type Category Bitmap
* - \ref CARD_TYPE_bmHC
* - \ref CARD_TYPE_bmSDMMC
* - \ref CARD_TYPE_bmUNKNOWN
* - \ref CARD_TYPE_bmSD
* - \ref CARD_TYPE_bmMMC
* - \ref CARD_TYPE_bmSDIO
* - Card Types
* - \ref CARD_UNKNOWN
* - \ref CARD_SD
* - \ref CARD_SDHC
* - \ref CARD_MMC
* - \ref CARD_MMCHD
* - \ref CARD_SDIO
* - \ref CARD_SDCOMBO
* - \ref CARD_SDHCCOMBO
* @{*/
#define CARD_TYPE_bmHC (1 << 0) /**< Bit for High-Capacity(Density) */
#define CARD_TYPE_bmSDMMC (0x3 << 1) /**< Bits mask for SD/MMC */
#define CARD_TYPE_bmUNKNOWN (0x0 << 1) /**< Bits for Unknown card */
#define CARD_TYPE_bmSD (0x1 << 1) /**< Bits for SD */
#define CARD_TYPE_bmMMC (0x2 << 1) /**< Bits for MMC */
#define CARD_TYPE_bmSDIO (1 << 3) /**< Bit for SDIO */
/** Card can not be identified */
#define CARD_UNKNOWN (0)
/** SD Card (0x2) */
#define CARD_SD (CARD_TYPE_bmSD)
/** SD High Capacity Card (0x3) */
#define CARD_SDHC (CARD_TYPE_bmSD|CARD_TYPE_bmHC)
/** MMC Card (0x4) */
#define CARD_MMC (CARD_TYPE_bmMMC)
/** MMC High-Density Card (0x5) */
#define CARD_MMCHD (CARD_TYPE_bmMMC|CARD_TYPE_bmHC)
/** SDIO only card (0x8) */
#define CARD_SDIO (CARD_TYPE_bmSDIO)
/** SDIO Combo, with SD embedded (0xA) */
#define CARD_SDCOMBO (CARD_TYPE_bmSDIO|CARD_SD)
/** SDIO Combo, with SDHC embedded (0xB) */
#define CARD_SDHCCOMBO (CARD_TYPE_bmSDIO|CARD_SDHC)
#include "ch_sdmmc_macros.h"
#include "ch_sdmmc_pmc.h"
#include "ch_sdmmc_trace.h"
extern const uint16_t sdmmcTransUnits[8];
extern const uint8_t sdTransMultipliers[16];
extern const uint8_t mmcTransMultipliers[16];
extern void SdParamReset(sSdCard * pSd);
extern uint32_t SdmmcDecodeTransSpeed(uint32_t code,
const uint16_t * unitCodes, const uint8_t * multiCodes);
#endif /*_CH_SDMMC_LIB_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,207 @@
#ifndef CH_SDMMC_CMDS_H_
#define CH_SDMMC_CMDS_H_
/**
* \struct SdCmd6Arg
* Argument for SD CMD6
*/
typedef struct _SdCmd6Arg {
uint32_t acc_mode:4, /**< [ 3: 0] function group 1, access mode */
cmd_sys:4, /**< [ 7: 4] function group 2, command system */
drv_strgth:4, /**< [11: 8] function group 3, driver strength */
pwr_limit:4, /**< [15:12] function group 4, power limit */
func_grp5:4, /**< [19:16] function group 5, 0xF or 0x0 */
func_grp6:4, /**< [23:20] function group 6, 0xF or 0x0 */
reserved:7, /**< [30:24] reserved 0 */
set:1; /**< [31 ] operation: 0 to check or 1 to set */
} SdCmd6Arg, SdSwitchArg;
/** \addtogroup sdmmc_struct_cmdarg SD/MMC command arguments
* Here lists the command arguments for SD/MMC.
* - CMD6 Argument
* - \ref MmcCmd6Arg "MMC CMD6"
* - \ref SdCmd6Arg "SD CMD6"
* - \ref SdioCmd52Arg CMD52
* - \ref SdioCmd53Arg CMD53
* @{*/
/**
* \struct MmcCmd6Arg
* Argument for MMC CMD6
*/
typedef struct _MmcCmd6Arg {
uint8_t access;
uint8_t index;
uint8_t value;
uint8_t cmdSet;
} MmcCmd6Arg, MmcSwitchArg;
/**
* \struct SdioCmd52Arg
* Argument for SDIO CMD52
*/
typedef struct _SdioCmd52Arg {
uint32_t data:8, /**< [ 7: 0] data for writing */
stuff0:1, /**< [ 8] reserved */
regAddress:17, /**< [25: 9] register address */
stuff1:1, /**< [ 26] reserved */
rawFlag:1, /**< [ 27] Read after Write flag */
functionNum:3, /**< [30:28] Number of the function */
rwFlag:1; /**< [ 31] Direction, 1:write, 0:read. */
} SdioCmd52Arg, SdioRwDirectArg;
#define SDMMC_CMD_bmPOWERON (0x1 ) /**< Do Power ON sequence */
#define SDMMC_CMD_bmCOMMAND (0x1 << 1) /**< Send command */
#define SDMMC_CMD_bmDATAMASK (0x3 << 2) /**< Data operation mask */
#define SDMMC_CMD_bmNODATA (0x0 << 2) /**< No data transfer */
#define SDMMC_CMD_RX 0x1 /**< data RX */
#define SDMMC_CMD_bmDATARX (0x1 << 2) /**< Bits for data RX */
#define SDMMC_CMD_TX 0x2 /**< data TX */
#define SDMMC_CMD_bmDATATX (0x2 << 2) /**< Bits for data TX */
#define SDMMC_CMD_STOPXFR 0x3 /**< data stop */
#define SDMMC_CMD_bmSTOPXFR (0x3 << 2) /**< Bits for transfer stop */
#define SDMMC_CMD_bmRESPMASK (0x7 << 4) /**< Bits masks response option */
#define SDMMC_CMD_bmRESP(R) (((R)&0x7) << 4) /**< Bits setup response type: 1 for R1, 2 for R2, ... 7 for R7 */
/** \addtogroup sdmmc_status_bm SD/MMC Status register constants
* @{*/
#define STATUS_APP_CMD (1UL << 5)
#define STATUS_SWITCH_ERROR (1UL << 7)
#define STATUS_READY_FOR_DATA (1UL << 8)
#define STATUS_IDLE (0UL << 9)
#define STATUS_READY (1UL << 9)
#define STATUS_IDENT (2UL << 9)
#define STATUS_STBY (3UL << 9)
#define STATUS_TRAN (4UL << 9)
#define STATUS_DATA (5UL << 9)
#define STATUS_RCV (6UL << 9)
#define STATUS_PRG (7UL << 9)
#define STATUS_DIS (8UL << 9)
#define STATUS_BTST (9UL << 9)
#define STATUS_SLEEP (10UL << 9)
#define STATUS_STATE (0xFUL << 9)
#define STATUS_ERASE_RESET (1UL << 13)
#define STATUS_WP_ERASE_SKIP (1UL << 15)
#define STATUS_CIDCSD_OVERWRITE (1UL << 16)
#define STATUS_OVERRUN (1UL << 17)
#define STATUS_UNERRUN (1UL << 18)
#define STATUS_ERROR (1UL << 19)
#define STATUS_CC_ERROR (1UL << 20)
#define STATUS_CARD_ECC_FAILED (1UL << 21)
#define STATUS_ILLEGAL_COMMAND (1UL << 22)
#define STATUS_COM_CRC_ERROR (1UL << 23)
#define STATUS_UN_LOCK_FAILED (1UL << 24)
#define STATUS_CARD_IS_LOCKED (1UL << 25)
#define STATUS_WP_VIOLATION (1UL << 26)
#define STATUS_ERASE_PARAM (1UL << 27)
#define STATUS_ERASE_SEQ_ERROR (1UL << 28)
#define STATUS_BLOCK_LEN_ERROR (1UL << 29)
#define STATUS_ADDRESS_MISALIGN (1UL << 30)
#define STATUS_ADDR_OUT_OR_RANGE (1UL << 31)
#define SD_OCR_S18A (1ul << 24) /**< Switching to 1.8V signaling level Accepted */
#define SDIO_OCR_MP (0x1ul << 27) /**< SDIO: Memory present */
#define SDIO_OCR_NF (0x3ul << 28) /**< SDIO: Number of functions */
#define MMC_OCR_ACCESS_MODE (0x3ul << 29) /**< MMC: Access mode, 0x2 is sector mode */
#define MMC_OCR_ACCESS_BYTE (0x0 << 29) /**< MMC: Byte access mode */
#define MMC_OCR_ACCESS_SECTOR (0x2ul << 29) /**< MMC: Sector access mode */
#define SD_OCR_UHS_II (1ul << 29) /**< SD: UHS-II Card Status */
#define SD_OCR_CCS (1ul << 30) /**< SD: Card Capacity Status */
#define SD_OCR_BUSYN (1ul << 31) /**< SD/MMC: Busy Status */
/** \addtogroup sdmmc_sd_status SD/MMC status fields
* @{
*/
/** SSR (SD Status) access macros (512 bits, 16 * 32 bits, 64 * 8 bits). */
#define SD_ST(pSt, field, bits) SD_GetField(pSt, 512, field, bits)
#define SD_SSR_DAT_BUS_WIDTH(pSt) (uint8_t)SD_ST(pSt, 510, 2) /**< Bus width, 00: default, 10:4-bit */
#define SD_SSR_DATA_BUS_WIDTH_1BIT 0x0 /**< 1-bit bus width */
#define SD_SSR_DATA_BUS_WIDTH_4BIT 0x2 /**< 4-bit bus width */
#define SD_SSR_SECURED_MODE(pSt) (uint8_t)SD_ST(pSt, 509, 1) /**< Secured Mode */
#define SD_SSR_CARD_TYPE(pSt) (uint16_t)SD_ST(pSt, 480, 16)
#define SD_SSR_CARD_TYPE_RW 0x0000 /**< Regular SD R/W Card */
#define SD_SSR_CARD_TYPE_ROM 0x0001 /**< SD ROM Card */
#define SD_SSR_CARD_TYPE_OTP 0x0002 /**< OTP SD Card */
#define SD_SSR_SIZE_OF_PROTECTED_AREA(pSt) SD_ST(pSt, 448, 32) /**< STD: ThisSize*Multi*BlockLen, HC: Size in bytes */
#define SD_SSR_SPEED_CLASS(pSt) (uint8_t)SD_ST(pSt, 440, 8) /**< Speed Class, value can be calculated by Pw/2 */
#define SD_SSR_SPEED_CLASS_0 0
#define SD_SSR_SPEED_CLASS_2 1 // >= 2MB/s
#define SD_SSR_SPEED_CLASS_4 2 // >= 4MB/s
#define SD_SSR_SPEED_CLASS_6 3 // >= 6MB/s
#define SD_SSR_SPEED_CLASS_10 4 // >= 10MB/s
#define SD_SSR_PERFORMANCE_MOVE(pSt) (uint8_t)SD_ST(pSt, 432, 8) /**< 8-bit, by 1MB/s step. */
#define SD_SSR_AU_SIZE(pSt) (uint8_t)SD_ST(pSt, 428, 4) /**< AU Size, in power of 2 from 16KB */
#define SD_SSR_AU_SIZE_16K 1
#define SD_SSR_AU_SIZE_32K 2
#define SD_SSR_AU_SIZE_64K 3
#define SD_SSR_AU_SIZE_128K 4
#define SD_SSR_AU_SIZE_256K 5
#define SD_SSR_AU_SIZE_512K 6
#define SD_SSR_AU_SIZE_1M 7
#define SD_SSR_AU_SIZE_2M 8
#define SD_SSR_AU_SIZE_4M 9
#define SD_SSR_AU_SIZE_8M 0xa
#define SD_SSR_AU_SIZE_12M 0xb
#define SD_SSR_AU_SIZE_16M 0xc
#define SD_SSR_AU_SIZE_24M 0xd
#define SD_SSR_AU_SIZE_32M 0xe
#define SD_SSR_AU_SIZE_64M 0xf
#define SD_SSR_ERASE_SIZE(pSt) (uint16_t)SD_ST(pSt, 408, 16) /**< 16-bit, number of AUs erased. */
#define SD_SSR_ERASE_TIMEOUT(pSt) (uint8_t)SD_ST(pSt, 402, 6) /**< Timeout value for erasing areas */
#define SD_SSR_ERASE_OFFSET(pSt) (uint8_t)SD_ST(pSt, 400, 2) /**< Fixed offset value added to erase time */
#define SD_SSR_UHS_SPEED_GRADE(pSt) (uint8_t)SD_ST(pSt, 396, 4) /**< Speed Grade for UHS mode */
#define SD_SSR_SPEED_GRADE_0 0x0
#define SD_SSR_SPEED_GRADE_1 0x1
#define SD_SSR_SPEED_GRADE_3 0x3
#define SD_SSR_UHS_AU_SIZE(pSt) (uint8_t)SD_ST(pSt, 392, 4) /**< Size of AU for UHS mode */
#define SD_SSR_UHS_AU_SIZE_UNDEF 0
#define SD_SSR_UHS_AU_SIZE_1M 0x7
#define SD_SSR_UHS_AU_SIZE_2M 0x8
#define SD_SSR_UHS_AU_SIZE_4M 0x9
#define SD_SSR_UHS_AU_SIZE_8M 0xa
#define SD_SSR_UHS_AU_SIZE_12M 0xb
#define SD_SSR_UHS_AU_SIZE_16M 0xc
#define SD_SSR_UHS_AU_SIZE_24M 0xd
#define SD_SSR_UHS_AU_SIZE_32M 0xe
#define SD_SSR_UHS_AU_SIZE_64M 0xf
extern uint8_t tuneSampling(SdmmcDriver *driver);
extern uint8_t CancelCommand(SdmmcDriver *driver);
extern uint8_t CmdPowerOn(SdmmcDriver *drv);
extern uint8_t SdCmd6(SdmmcDriver *drv,
const SdCmd6Arg * pSwitchArg, uint8_t * pStatus, uint32_t * pResp);
extern uint8_t SdCmd8(SdmmcDriver *drv, uint8_t supplyVoltage);
extern uint8_t Acmd6(SdmmcDriver *pSd, uint8_t busWidth);
extern uint8_t Acmd13(SdmmcDriver *drv, uint8_t * pSSR, uint32_t * pResp);
extern uint8_t Acmd41(SdmmcDriver *drv, bool * low_sig_lvl, bool * hc);
extern uint8_t Acmd51(SdmmcDriver *drv, uint8_t * pSCR, uint32_t * pResp);
extern uint8_t Cmd0(SdmmcDriver *drv, uint8_t arg);
extern uint8_t Cmd1(SdmmcDriver *drv, bool * hc);
extern uint8_t Cmd2(SdmmcDriver *drv);
extern uint8_t Cmd3(SdmmcDriver *drv);
extern uint8_t Cmd5(SdmmcDriver *drv, uint32_t * pIo);
extern uint8_t Cmd7(SdmmcDriver *drv, uint16_t address);
extern uint8_t Cmd9(SdmmcDriver *drv);
extern uint8_t Cmd11(SdmmcDriver *drv, uint32_t * pStatus);
extern uint8_t Cmd13(SdmmcDriver *drv, uint32_t * pStatus);
extern uint8_t Cmd14(SdmmcDriver *drv, uint8_t * pData, uint8_t len, uint32_t * pStatus);
extern uint8_t Cmd16(SdmmcDriver *drv, uint16_t blkLen);
extern uint8_t Cmd19(SdmmcDriver *drv, uint8_t * pData, uint8_t len, uint32_t * pStatus);
extern uint8_t Cmd52(SdmmcDriver *drv,uint8_t wrFlag,uint8_t funcNb, uint8_t rdAfterWr, uint32_t addr, uint32_t * pIoData);
extern uint8_t Cmd55(SdmmcDriver *drv, uint16_t cardAddr);
extern uint8_t MmcCmd8(SdmmcDriver *drv);
extern uint8_t MmcCmd6(SdmmcDriver *drv, const void *pSwitchArg, uint32_t * pResp);
#endif /* CH_SDMMC_CMDS_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,392 @@
#ifndef CH_SDMMC_DEVICE_H_
#define CH_SDMMC_DEVICE_H_
/** \addtogroup sdmmc_ocr_acc SD/MMC OCR register fields (SD 2.0 & MMC 4.3)
* @{
*/
#define SD_OCR_VDD_LOW (1ul << 7) /**< SD: Reserved for Low Voltage Range */
#define MMC_OCR_VDD_170_195 (1ul << 7) /**< MMC: 1.7 ~ 1.95V, Dual vol and eMMC is 1 */
#define MMC_OCR_VDD_200_270 (0x7Ful << 8) /**< MMC: 2.0 ~ 2.7 V */
#define SD_OCR_VDD_20_21 (1ul << 8)
#define SD_OCR_VDD_21_22 (1ul << 9)
#define SD_OCR_VDD_22_23 (1ul << 10)
#define SD_OCR_VDD_23_24 (1ul << 11)
#define SD_OCR_VDD_24_25 (1ul << 12)
#define SD_OCR_VDD_25_26 (1ul << 13)
#define SD_OCR_VDD_26_27 (1ul << 14)
#define SD_OCR_VDD_27_28 (1ul << 15)
#define SD_OCR_VDD_28_29 (1ul << 16)
#define SD_OCR_VDD_29_30 (1ul << 17)
#define SD_OCR_VDD_30_31 (1ul << 18)
#define SD_OCR_VDD_31_32 (1ul << 19)
#define SD_OCR_VDD_32_33 (1ul << 20)
#define SD_OCR_VDD_33_34 (1ul << 21)
#define SD_OCR_VDD_34_35 (1ul << 22)
#define SD_OCR_VDD_35_36 (1ul << 23)
/**
* sdmmc_speedmode SD/MMC Bus speed modes
* Here lists the MMC, e.MMC and SD bus speed modes.
*/
#define SDMMC_TIM_MMC_BC (0x00)
#define SDMMC_TIM_MMC_HS_SDR (0x01)
#define SDMMC_TIM_MMC_HS_DDR (0x02)
#define SDMMC_TIM_MMC_HS200 (0x03)
#define SDMMC_TIM_SD_DS (0x10)
#define SDMMC_TIM_SD_HS (0x11)
#define SDMMC_TIM_SD_SDR12 (0x12)
#define SDMMC_TIM_SD_SDR25 (0x13)
#define SDMMC_TIM_SD_SDR50 (0x14)
#define SDMMC_TIM_SD_DDR50 (0x15)
#define SDMMC_TIM_SD_SDR104 (0x16)
/**
* \addtogroup sdmmc_powermode SD/MMC power supply modes
* Here we list the voltage level configurations we may apply when supplying
* power to the device.
* @{*/
#define SDMMC_PWR_OFF (0)
#define SDMMC_PWR_STD (1)
#define SDMMC_PWR_STD_VDD_LOW_IO (2)
/** SD/MMC Low Level IO Control: Check busy.
Must implement for low level driver busy check.
IOCtrl(pSd, SDMMC_IOCTL_BUSY_CHECK, (uint32_t)pBusyFlag) */
#define SDMMC_IOCTL_BUSY_CHECK 0x0
/** SD/MMC Low Level IO Control: Power control.
Recommended for SD/MMC/SDIO power control.
IOCtrl(pSd, SDMMC_IOCTL_POWER, (uint32_t)ON/OFF) */
#define SDMMC_IOCTL_POWER 0x1
/** SD/MMC Low Level IO Control: Cancel command.
IOCtrl(pSd, SDMMC_IOCTL_CANCEL_CMD, NULL) */
#define SDMMC_IOCTL_CANCEL_CMD 0x2
/** SD/MMC Low Level IO Control: Reset & disable HW.
IOCtrl(pSd, SDMMC_IOCTL_RESET, NULL) */
#define SDMMC_IOCTL_RESET 0x3
/** SD/MMC Low Level IO Control: Set clock frequency, return applied frequency
Recommended for clock selection
IOCtrl(pSd, SDMMC_IOCTL_SET_CLOCK, (uint32_t*)pIoFreq) */
#define SDMMC_IOCTL_SET_CLOCK 0x11
/** SD/MMC Low Level IO Control: Set bus mode, return applied mode
Recommended for bus mode selection
IOCtrl(pSd, SDMMC_IOCTL_SET_BUSMODE, (uint32_t*)pIoBusMode) */
#define SDMMC_IOCTL_SET_BUSMODE 0x12
/** SD/MMC Low Level IO Control: Select one of the SDMMC_TIM_x timing modes.
Returns the effective mode, further to this operation.
IOCtrl(pSd, SDMMC_IOCTL_SET_HSMODE, (uint32_t*)pIoTimingMode) */
#define SDMMC_IOCTL_SET_HSMODE 0x13
/** SD/MMC Low Level IO Control: Set Boot mode */
#define SDMMC_IOCTL_SET_BOOTMODE 0x14
/** SD/MMC Low Level IO Control: Enable or disable implicit SET_BLOCK_COUNT
command, return applied mode.
Recommended with devices that support the SET_BLOCK_COUNT command.
IOCtrl(pSd, SDMMC_IOCTL_SET_LENPREFIX, (uint32_t*)pIoLenPrefix) */
#define SDMMC_IOCTL_SET_LENPREFIX 0x15
/** SD/MMC Low Level IO Control: Get clock frequency */
#define SDMMC_IOCTL_GET_CLOCK 0x21
/** SD/MMC Low Level IO Control: Bus mode */
#define SDMMC_IOCTL_GET_BUSMODE 0x22
/** SD/MMC Low Level IO Control: Query whether the driver supports the specified
SDMMC_TIM_x timing mode. */
#define SDMMC_IOCTL_GET_HSMODE 0x23
/** SD/MMC Low Level IO Control: Boot mode */
#define SDMMC_IOCTL_GET_BOOTMODE 0x24
/** SD/MMC Low Level IO Control: Query driver capability, whether the driver
implicitly sends the STOP_TRANSMISSION command when multiple-block data
transfers complete successfully.
IOCtrl(pSd, SDMMC_IOCTL_GET_XFERCOMPL, (uint32_t*)pOAutoXferCompletion) */
#define SDMMC_IOCTL_GET_XFERCOMPL 0x25
/** SD/MMC Low Level IO Control: Query whether a device is detected in this slot
IOCtrl(pSd, SDMMC_IOCTL_GET_DEVICE, (uint32_t*)pODetected) */
#define SDMMC_IOCTL_GET_DEVICE 0x26
/** SD/MMC Low Level IO Control: Query whether the card is writeprotected
or not by mechanical write protect switch */
#define SDMMC_IOCTL_GET_WP 0x27
/** @}*/
#define SD_IFC_CHK_PATTERN_Pos 0 /**< Check pattern */
#define SD_IFC_CHK_PATTERN_Msk (0xffu << SD_IFC_CHK_PATTERN_Pos)
#define SD_IFC_CHK_PATTERN_STD (0xaau << 0)
#define SD_IFC_VHS_Pos 8 /**< Host Supplied Voltage range */
#define SD_IFC_VHS_Msk (0xfu << SD_IFC_VHS_Pos)
#define SD_IFC_VHS_27_36 (0x1u << 8)
#define SD_IFC_VHS_LOW (0x2u << 8)
/** Get u8 from byte pointed data area */
#define SD_U8(pD, nBytes, iByte) ( ((uint8_t*)(pD))[(iByte)] )
/** Get u16 from data area */
#define SD_U16(pD, nBytes, iByte) \
( (uint16_t)((uint8_t*)(pD))[(iByte)] |\
(uint16_t)((uint8_t*)(pD))[(iByte) + 1] << 8 )
/**Get u32 from data area */
#define SD_U32(pD, nBytes, iByte) \
( (uint32_t)((uint8_t*)(pD))[(iByte)] |\
(uint32_t)((uint8_t*)(pD))[(iByte) + 1] << 8 |\
(uint32_t)((uint8_t*)(pD))[(iByte) + 2] << 16 |\
(uint32_t)((uint8_t*)(pD))[(iByte) + 3] << 24 )
/** \addtogroup mmc_ext_csd MMC Extended CSD byte fields
* @{
*/
/** MMC Extended CSD access macro: get one byte (512 bytes). */
#define MMC_EXT8(p, i) SD_U8(p, 512, i)
/** MMC Extended CSD access macro: get one word (512 bytes). */
#define MMC_EXT32(p, i) SD_U32(p, 512, i)
#define MMC_EXT_S_CMD_SET_I 504 /**< Supported Command Sets slice */
#define MMC_EXT_S_CMD_SET(p) MMC_EXT8(p, MMC_EXT_S_CMD_SET_I)
#define MMC_EXT_PWR_CL_DDR_52_360_I 239 /**< Power Class for 52MHz DDR @ 3.6V */
#define MMC_EXT_PWR_CL_DDR_52_360(p) MMC_EXT8(p, MMC_EXT_PWR_CL_DDR_52_360_I)
#define MMC_EXT_PWR_CL_200_195_I 237 /**< Power Class for 200MHz HS200 @ VCCQ=1.95V VCC=3.6V */
#define MMC_EXT_PWR_CL_200_195(p) MMC_EXT8(p, MMC_EXT_PWR_CL_200_195_I)
#define MMC_EXT_BOOT_INFO_I 228 /**< Boot information slice */
#define MMC_EXT_BOOT_INFO(p) MMC_EXT8(p, MMC_EXT_BOOT_INFO_I)
#define MMC_EXT_BOOT_SIZE_MULTI_I 226 /**< Boot partition size slice */
#define MMC_EXT_BOOT_SIZE_MULTI(p) MMC_EXT8(p, MMC_EXT_BOOT_SIZE_MULTI_I)
#define MMC_EXT_ACC_SIZE_I 225 /**< Access size slice */
#define MMC_EXT_ACC_SIZE(p) MMC_EXT8(p, MMC_EXT_ACC_SIZE_I)
#define MMC_EXT_HC_ERASE_GRP_SIZE_I 224 /**< High-capacity erase time unit size slice */
#define MMC_EXT_HC_ERASE_GRP_SIZE(p) MMC_EXT8(p, MMC_EXT_HC_ERASE_GRP_SIZE_I)
#define MMC_EXT_ERASE_TIMEOUT_MULT_I 223 /**< High-capacity erase timeout slice */
#define MMC_EXT_ERASE_TIMEOUT_MULT(p) MMC_EXT8(p, MMC_EXT_ERASE_TIMEOUT_MULT_I)
#define MMC_EXT_REL_WR_SEC_C_I 222 /**< Reliable write sector count slice */
#define MMC_EXT_REL_WR_SEC_C(p) MMC_EXT8(p, MMC_EXT_REL_WR_SEC_C_I)
#define MMC_EXT_HC_WP_GRP_SIZE_I 221 /**< High-capacity write protect group size slice */
#define MMC_EXT_HC_WP_GRP_SIZE(p) MMC_EXT8(p, MMC_EXT_HC_WP_GRP_SIZE_I)
#define MMC_EXT_S_C_VCC_I 220 /**< Sleep current (VCC) */
#define MMC_EXT_S_C_VCC(p) MMC_EXT8(p, MMC_EXT_S_C_VCC_I)
#define MMC_EXT_S_C_VCCQ_I 219 /**< Sleep current (VCC) */
#define MMC_EXT_S_C_VCCQ(p) MMC_EXT8(p, MMC_EXT_S_C_VCCQ_I)
#define MMC_EXT_S_A_TIMEOUT_I 217 /**< Sleep current (VCCQ) */
#define MMC_EXT_S_A_TIMEOUT(p) MMC_EXT8(p, MMC_EXT_S_A_TIMEOUT_I)
#define MMC_EXT_SEC_COUNT_I 212 /**< Sector Count slice */
#define MMC_EXT_SEC_COUNT(p) MMC_EXT32(p, MMC_EXT_SEC_COUNT_I)
#define MMC_EXT_MIN_PERF_W_8_52_I 210 /**< Minimum Write Performance for 8bit @ 52MHz */
#define MMC_EXT_MIN_PERF_W_8_52(p) MMC_EXT8(p, MMC_EXT_MIN_PERF_W_8_52_I)
#define MMC_EXT_MIN_PERF_R_8_52_I 209 /**< Minimum Read Performance for 8bit @ 52MHz */
#define MMC_EXT_MIN_PERF_R_8_52(p) MMC_EXT8(p, MMC_EXT_MIN_PERF_R_8_52_I)
#define MMC_EXT_MIN_PERF_W_8_26_4_52_I 208 /**< Minimum Write Performance for 8bit @ 26MHz or 4bit @ 52MHz */
#define MMC_EXT_MIN_PERF_W_8_26_4_52(p) MMC_EXT8(p, MMC_EXT_MIN_PERF_W_8_26_4_52_I)
#define MMC_EXT_MIN_PERF_R_8_26_4_52_I 207 /**< Minimum Read Performance for 8bit @ 26MHz or 4bit @ 52MHz */
#define MMC_EXT_MIN_PERF_R_8_26_4_52(p) MMC_EXT8(p, MMC_EXT_MIN_PERF_R_8_26_4_52_I)
#define MMC_EXT_MIN_PERF_W_4_26_I 206 /**< Minimum Write Performance for 4bit @ 26MHz */
#define MMC_EXT_MIN_PERF_W_4_26(p) MMC_EXT8(p, MMC_EXT_MIN_PERF_W_4_26_I)
#define MMC_EXT_MIN_PERF_R_4_26_I 205 /**< Minimum Read Performance for 4bit @ 26MHz */
#define MMC_EXT_MIN_PERF_R_4_26(p) MMC_EXT8(p, MMC_EXT_MIN_PERF_R_4_26_I)
#define MMC_EXT_PWR_CL_26_360_I 203 /**< Power Class for 26MHz @ 3.6V */
#define MMC_EXT_PWR_CL_26_360(p) MMC_EXT8(p, MMC_EXT_PWR_CL_26_360_I)
#define MMC_EXT_PWR_CL_52_360_I 202 /**< Power Class for 52MHz @ 3.6V */
#define MMC_EXT_PWR_CL_52_360(p) MMC_EXT8(p, MMC_EXT_PWR_CL_52_360_I)
#define MMC_EXT_PWR_CL_26_195_I 201 /**< Power Class for 26MHz @ 1.95V */
#define MMC_EXT_PWR_CL_26_195(p) MMC_EXT8(p, MMC_EXT_PWR_CL_26_195_I)
#define MMC_EXT_PWR_CL_52_195_I 200 /**< Power Class for 52MHz @ 1.95V */
#define MMC_EXT_PWR_CL_52_195(p) MMC_EXT8(p, MMC_EXT_PWR_CL_52_195_I)
#define MMC_EXT_DRV_STRENGTH_I 197 /**< Supported I/O driver strength types */
#define MMC_EXT_DRV_STRENGTH(p) MMC_EXT8(p, MMC_EXT_DRV_STRENGTH_I)
#define MMC_EXT_CARD_TYPE_I 196 /**< Card Type */
#define MMC_EXT_CARD_TYPE(p) MMC_EXT8(p, MMC_EXT_CARD_TYPE_I)
#define MMC_EXT_CSD_STRUCTURE_I 194 /**< CSD Structure Version */
#define MMC_EXT_CSD_STRUCTURE(p) MMC_EXT8(p, MMC_EXT_CSD_STRUCTURE_I)
#define MMC_EXT_EXT_CSD_REV_I 192 /**< Extended CSD Revision */
#define MMC_EXT_EXT_CSD_REV(p) MMC_EXT8(p, MMC_EXT_EXT_CSD_REV_I)
#define MMC_EXT_CMD_SET_I 191 /**< Command Set */
#define MMC_EXT_CMD_SET(p) MMC_EXT8(p, MMC_EXT_CMD_SET_I)
#define MMC_EXT_CMD_SET_REV_I 189 /**< Command Set Revision */
#define MMC_EXT_CMD_SET_REV(p) MMC_EXT8(p, MMC_EXT_CMD_SET_REV_I)
#define MMC_EXT_POWER_CLASS_I 187 /**< Power Class */
#define MMC_EXT_POWER_CLASS(p) MMC_EXT8(p, MMC_EXT_POWER_CLASS_I)
#define MMC_EXT_HS_TIMING_I 185 /**< High Speed Interface Timing */
#define MMC_EXT_HS_TIMING(p) MMC_EXT8(p, MMC_EXT_HS_TIMING_I)
#define MMC_EXT_HS_TIMING_HS400 0x3
#define MMC_EXT_HS_TIMING_HS200 0x2
#define MMC_EXT_HS_TIMING_EN 0x1
#define MMC_EXT_HS_TIMING_DIS 0x0
#define MMC_EXT_HS_TIMING_40R 0x40
#define MMC_EXT_HS_TIMING_100R 0x30
#define MMC_EXT_HS_TIMING_66R 0x20
#define MMC_EXT_HS_TIMING_33R 0x10
#define MMC_EXT_HS_TIMING_50R 0x00
#define MMC_EXT_BUS_WIDTH_I 183 /**< Bus Width Mode */
#define MMC_EXT_BUS_WIDTH(p) MMC_EXT8(p, MMC_EXT_BUS_WIDTH_I)
#define MMC_EXT_BUS_WIDTH_1BIT 0
#define MMC_EXT_BUS_WIDTH_4BITS 1
#define MMC_EXT_BUS_WIDTH_8BITS 2
#define MMC_EXT_BUS_WIDTH_DDR 0x4
#define MMC_EXT_ERASED_MEM_CONT_I 181 /**< Erased Memory Content */
#define MMC_EXT_ERASED_MEM_CONT(p) MMC_EXT8(p, MMC_EXT_ERASED_MEM_CONT_I)
#define MMC_EXT_BOOT_CONFIG_I 179 /**< Boot configuration slice */
#define MMC_EXT_BOOT_CONFIG(p) MMC_EXT8(p, MMC_EXT_BOOT_CONFIG_I)
#define MMC_EXT_BOOT_BUS_WIDTH_I 177 /**< Boot bus width slice */
#define MMC_EXT_BOOT_BUS_WIDTH(p) MMC_EXT8(p, MMC_EXT_BOOT_BUS_WIDTH_I)
#define MMC_EXT_ERASE_GROUP_DEF_I 175 /**< High-density erase group definition */
#define MMC_EXT_ERASE_GROUP_DEF(p) MMC_EXT8(p, MMC_EXT_ERASE_GROUP_DEF_I)
#define MMC_EXT_BOOT_WP_STATUS_I 174 /**< Current protection status of the boot partitions */
#define MMC_EXT_BOOT_WP_STATUS(p) MMC_EXT8(p, MMC_EXT_BOOT_WP_STATUS_I)
#define MMC_EXT_DATA_SECTOR_SIZE_I 61 /**< Current sector size */
#define MMC_EXT_DATA_SECTOR_SIZE(p) MMC_EXT8(p, MMC_EXT_DATA_SECTOR_SIZE_I)
#define MMC_EXT_DATA_SECT_512B 0
#define MMC_EXT_DATA_SECT_4KIB 1
#define SD_BITS32(pDw, nbits, ibit, bits) \
( (((uint32_t*)(pDw))[(nbits)/32-(ibit)/32-1] >> ((ibit)%32)) & ((uint32_t)(1ul << (bits)) - 1 ) )
/** \addtogroup sdmmc_cid_acc SD/MMC CID register fields
* @{
*/
/** CID register access (128 bits, 16 * 8 bits, 4 * 32 bits) */
#define SD_CID(pCid, field, bits) SD_BITS32(pCid, 128, field, bits)
#define SD_CID_MID(pCid) (uint8_t)SD_CID(pCid, 120, 8) /**< Manufacture ID */
#define eMMC_CID_CBX(pCid) (uint8_t)SD_CID(pCid, 112, 2) /**< eMMC BGA(01)/CARD(00) */
#define SD_CID_OID1(pCid) (uint8_t)SD_CID(pCid, 112, 8) /**< OEM/App ID Byte 1 */
#define SD_CID_OID0(pCid) (uint8_t)SD_CID(pCid, 104, 8) /**< OEM/App ID Byte 0 */
#define MMC_CID_OID(pCid) (uint16_t)SD_CID(pCid, 104, 16) /**< MMC OEM/App ID */
#define eMMC_CID_OID(pCid) (uint8_t)SD_CID(pCid, 104, 8) /**< MMC v4.3+ OEM/App ID */
#define SD_CID_PNM4(pCid) (uint8_t)SD_CID(pCid, 96, 8) /**< Product name byte 4 */
#define SD_CID_PNM3(pCid) (uint8_t)SD_CID(pCid, 88, 8) /**< Product name byte 3 */
#define SD_CID_PNM2(pCid) (uint8_t)SD_CID(pCid, 80, 8) /**< Product name byte 2 */
#define SD_CID_PNM1(pCid) (uint8_t)SD_CID(pCid, 72, 8) /**< Product name byte 1 */
#define SD_CID_PNM0(pCid) (uint8_t)SD_CID(pCid, 64, 8) /**< Product name byte 0 */
#define MMC_CID_PNM5(pCid) (uint8_t)SD_CID(pCid, 96, 8) /**< Product name byte 5 */
#define MMC_CID_PNM4(pCid) (uint8_t)SD_CID(pCid, 88, 8) /**< Product name byte 4 */
#define MMC_CID_PNM3(pCid) (uint8_t)SD_CID(pCid, 80, 8) /**< Product name byte 3 */
#define MMC_CID_PNM2(pCid) (uint8_t)SD_CID(pCid, 72, 8) /**< Product name byte 2 */
#define MMC_CID_PNM1(pCid) (uint8_t)SD_CID(pCid, 64, 8) /**< Product name byte 1 */
#define MMC_CID_PNM0(pCid) (uint8_t)SD_CID(pCid, 56, 8) /**< Product name byte 0 */
#define SD_CID_PRV1(pCid) (uint8_t)SD_CID(pCid, 60, 4) /**< Product revision major number */
#define SD_CID_PRV0(pCid) (uint8_t)SD_CID(pCid, 56, 4) /**< Product revision minor number */
#define MMC_CID_PRV1(pCid) (uint8_t)SD_CID(pCid, 52, 4) /**< Product revision major number */
#define MMC_CID_PRV0(pCid) (uint8_t)SD_CID(pCid, 48, 4) /**< Product revision minor number */
#define SD_CID_PSN3(pCid) (uint8_t)SD_CID(pCid, 48, 8) /**< Product serial 3 */
#define SD_CID_PSN2(pCid) (uint8_t)SD_CID(pCid, 40, 8) /**< Product serial 2 */
#define SD_CID_PSN1(pCid) (uint8_t)SD_CID(pCid, 32, 8) /**< Product serial 1 */
#define SD_CID_PSN0(pCid) (uint8_t)SD_CID(pCid, 24, 8) /**< Product serial 0 */
#define MMC_CID_PSN3(pCid) (uint8_t)SD_CID(pCid, 40, 8) /**< Product serial 3 */
#define MMC_CID_PSN2(pCid) (uint8_t)SD_CID(pCid, 32, 8) /**< Product serial 2 */
#define MMC_CID_PSN1(pCid) (uint8_t)SD_CID(pCid, 24, 8) /**< Product serial 1 */
#define MMC_CID_PSN0(pCid) (uint8_t)SD_CID(pCid, 16, 8) /**< Product serial 0 */
#define SD_CID_MDT_Y(pCid) (uint8_t)SD_CID(pCid, 12, 8) /**< Manufacturing Year (0=2000) */
#define SD_CID_MDT_M(pCid) (uint8_t)SD_CID(pCid, 8, 4) /**< Manufacturing month */
#define MMC_CID_MDT_Y(pCid) (uint8_t)SD_CID(pCid, 8, 4) /**< Manufacturing Year (0=1997 or 2013) */
#define MMC_CID_MDT_M(pCid) (uint8_t)SD_CID(pCid, 12, 4) /**< Manufacturing month */
#define SD_CID_CRC(pCid) (uint8_t)SD_CID(pCid, 1, 7) /**< CRC7 checksum */
/** @}*/
/** CSD register access macros (128 bits, 16 * 8 bits, 4 * 32 bits */
#define SD_CSD(pCsd, field, bits) SD_BITS32(pCsd, 128, field, bits)
#define SD_CSD_STRUCTURE(pCsd) (uint8_t)SD_CSD(pCsd, 126, 2) /**< CSD structure */
#define SD_CSD_STRUCTURE_1_0 0 /**< SD v1.01~1.10, v2.0/Std Capacity */
#define SD_CSD_STRUCTURE_2_0 1 /**< SD v2.0/HC */
#define MMC_CSD_STRUCTURE_1_0 0 /**< MMC v1.0~1.2 */
#define MMC_CSD_STRUCTURE_1_1 1 /**< MMC v1.4~2.2 */
#define MMC_CSD_STRUCTURE_1_2 2 /**< MMC v3.1~3.31(v4.0), v4.1~(>v4.1) */
#define MMC_CSD_SPEC_VERS(pCsd) (uint8_t)SD_CSD(pCsd, 122, 4) /**< System spec version */
#define MMC_CSD_SPEC_VERS_1_0 0 /**< MMC v1.0~1.2 */
#define MMC_CSD_SPEC_VERS_1_4 1 /**< MMC v1.4 */
#define MMC_CSD_SPEC_VERS_2_0 2 /**< MMC v2.0~2.2 */
#define MMC_CSD_SPEC_VERS_3_1 3 /**< MMC v3.1~3.31 */
#define MMC_CSD_SPEC_VERS_4_0 4 /**< MMC v4.0(v4.0), v4.1~(>v4.1) */
#define SD_CSD_TAAC(pCsd) (uint8_t)SD_CSD(pCsd, 112, 8) /**< Data read-access-time-1 */
#define SD_CSD_NSAC(pCsd) (uint8_t)SD_CSD(pCsd, 104, 8) /**< Data read access-time-2 in CLK cycles */
#define SD_CSD_TRAN_SPEED(pCsd) (uint8_t)SD_CSD(pCsd, 96, 8) /**< Max. data transfer rate */
#define SD_CSD_CCC(pCsd) (uint16_t)SD_CSD(pCsd, 84, 12) /**< Card command class */
#define SD_CSD_READ_BL_LEN(pCsd) (uint8_t)SD_CSD(pCsd, 80, 4) /**< Max. read data block length */
#define SD_CSD_READ_BL_PARTIAL(pCsd) (uint8_t)SD_CSD(pCsd, 79, 1) /**< Bartial blocks for read allowed */
#define SD_CSD_WRITE_BLK_MISALIGN(pCsd) (uint8_t)SD_CSD(pCsd, 78, 1) /**< Write block misalignment */
#define SD_CSD_READ_BLK_MISALIGN(pCsd) (uint8_t)SD_CSD(pCsd, 77, 1) /**< Read block misalignment */
#define SD_CSD_DSR_IMP(pCsd) (uint8_t)SD_CSD(pCsd, 76, 1) /**< DSP implemented */
#define SD_CSD_C_SIZE(pCsd) (uint16_t)((SD_CSD(pCsd, 72, 2) << 10) | \
(SD_CSD(pCsd, 64, 8) << 2) | \
SD_CSD(pCsd, 62, 2)) /**< Device size */
#define SD2_CSD_C_SIZE(pCsd) ((SD_CSD(pCsd, 64, 6) << 16) | \
(SD_CSD(pCsd, 56, 8) << 8) | \
SD_CSD(pCsd, 48, 8)) /**< Device size v2.0 */
#define SD_CSD_VDD_R_CURR_MIN(pCsd) (uint8_t)SD_CSD(pCsd, 59, 3) /**< Max. read current VDD min */
#define SD_CSD_VDD_R_CURR_MAX(pCsd) (uint8_t)SD_CSD(pCsd, 56, 3) /**< Max. read current VDD max */
#define SD_CSD_VDD_W_CURR_MIN(pCsd) (uint8_t)SD_CSD(pCsd, 53, 3) /**< Max. write current VDD min */
#define SD_CSD_VDD_W_CURR_MAX(pCsd) (uint8_t)SD_CSD(pCsd, 50, 3) /**< Max. write current VDD max */
#define SD_CSD_C_SIZE_MULT(pCsd) (uint8_t)SD_CSD(pCsd, 47, 3) /**< Device size multiplier */
#define SD_CSD_ERASE_BLK_EN(pCsd) (uint8_t)SD_CSD(pCsd, 46, 1) /**< Erase single block enable */
#define SD_CSD_SECTOR_SIZE(pCsd) (uint8_t)((SD_CSD(pCsd, 40, 6) << 1) | \
SD_CSD(pCsd, 39, 1)) /**< Erase sector size*/
#define SD_CSD_WP_GRP_SIZE(pCsd) (uint8_t)SD_CSD(pCsd, 32, 7) /**< Write protect group size*/
#define MMC_CSD_ERASE_GRP_SIZE(pCsd) (uint8_t)SD_CSD(pCsd, 42, 5) /**< Erase group size */
#define MMC_CSD_ERASE_GRP_MULT(pCsd) (uint8_t)SD_CSD(pCsd, 37, 5) /**< Erase group size multiplier */
#define MMC_CSD_WP_GRP_SIZE(pCsd) (uint8_t)SD_CSD(pCsd, 32, 5) /**< Write protect group size*/
#define SD_CSD_WP_GRP_ENABLE(pCsd) (uint8_t)SD_CSD(pCsd, 31, 1) /**< write protect group enable*/
#define MMC_CSD_DEFAULT_ECC(pCsd) (uint8_t)SD_CSD(pCsd, 29, 2) /**< Manufacturer default ECC */
#define SD_CSD_R2W_FACTOR(pCsd) (uint8_t)SD_CSD(pCsd, 26, 3) /**< Write speed factor*/
#define SD_CSD_WRITE_BL_LEN(pCsd) (uint8_t)((SD_CSD(pCsd, 24, 2) << 2) | \
SD_CSD(pCsd, 22, 2)) /**< Max write block length*/
#define SD_CSD_WRITE_BL_PARTIAL(pCsd) (uint8_t)SD_CSD(pCsd, 21, 1) /**< Partial blocks for write allowed*/
#define SD_CSD_CONTENT_PROT_APP(pCsd) (uint8_t)SD_CSD(pCsd, 16, 1) /**< File format group*/
#define SD_CSD_FILE_FORMAT_GRP(pCsd) (uint8_t)SD_CSD(pCsd, 15, 1) /**< File format group*/
#define SD_CSD_COPY(pCsd) (uint8_t)SD_CSD(pCsd, 14, 1) /**< Copy flag (OTP)*/
#define SD_CSD_PERM_WRITE_PROTECT(pCsd) (uint8_t)SD_CSD(pCsd, 13, 1) /**< Permanent write protect*/
#define SD_CSD_TMP_WRITE_PROTECT(pCsd) (uint8_t)SD_CSD(pCsd, 12, 1) /**< Temporary write protection*/
#define SD_CSD_FILE_FORMAT(pCsd) (uint8_t)SD_CSD(pCsd, 10, 2) /**< File format*/
#define MMC_CSD_ECC(pCsd) (uint8_t)SD_CSD(pCsd, 8, 2) /**< ECC */
#define MMC_CSD_ECC_NONE 0 /**< none */
#define MMC_CSD_ECC_BCH 1 /**< BCH, 3 correctable bits per block */
#define SD_CSD_CRC(pCsd) (uint8_t)SD_CSD(pCsd, 1, 7) /**< CRC*/
#define SD_CSD_MULT(pCsd) (uint16_t)(1u << (SD_CSD_C_SIZE_MULT(pCsd) + 2))
#define SD_CSD_BLOCKNR(pCsd) ((SD_CSD_C_SIZE(pCsd) + 1ul) * SD_CSD_MULT(pCsd))
#define SD_CSD_BLOCKNR_HC(pCsd) ((SD2_CSD_C_SIZE(pCsd) + 1ul) * 1024ull)
#define SD_CSD_BLOCK_LEN(pCsd) (uint16_t)(1u << SD_CSD_READ_BL_LEN(pCsd))
#define SD_CSD_TOTAL_SIZE(pCsd) ((uint64_t)SD_CSD_BLOCKNR(pCsd) * SD_CSD_BLOCK_LEN(pCsd))
#define SD_CSD_TOTAL_SIZE_HC(pCsd) ((SD2_CSD_C_SIZE(pCsd) + 1ul) * 512ull * 1024ull)
/** @}*/
#define STATUS_MMC_SWITCH ((uint32_t)( STATUS_CARD_IS_LOCKED \
| STATUS_COM_CRC_ERROR \
| STATUS_ILLEGAL_COMMAND \
| STATUS_CC_ERROR \
| STATUS_ERROR \
| STATUS_ERASE_RESET \
/*| STATUS_STATE*/ \
/*| STATUS_READY_FOR_DATA*/ \
| STATUS_SWITCH_ERROR ))
#define t_usleep(d,t) sdmmc_device_sleep(d,t,2)
#define t_msleep(d,t) sdmmc_device_sleep(d,t,1)
#define t_xsleep(d,t) sdmmc_device_sleep(d,t,0)
extern uint8_t sdmmc_device_lowlevelcfg(SdmmcDriver *driver);
extern bool sdmmc_device_initialize(SdmmcDriver *driver);
extern void sdmmc_device_deInit(SdmmcDriver *drv);
extern void sdmmc_device_poll(SdmmcDriver *driver);
extern uint32_t sdmmc_device_command(SdmmcDriver *driver);
extern uint32_t sdmmc_device_control(SdmmcDriver *driver, uint32_t bCtl);
extern void sdmmc_device_sleep(SdmmcDriver *driver,uint32_t t,uint32_t m);
extern void sdmmc_device_startTimeCount(SdmmcDriver *driver);
extern void sdmmc_device_checkTimeCount(SdmmcDriver *driver);
extern uint8_t sdmmc_device_start(SdmmcDriver *drv);
extern uint8_t sdmmc_device_identify(SdmmcDriver *drv);
extern uint8_t sdmmc_get_bus_width(SdmmcDriver *driver);
extern uint8_t HwSetHsMode(SdmmcDriver *drv, uint8_t timingMode);
extern uint32_t HwSetBusWidth( SdmmcDriver *drv,uint8_t newWidth);
extern uint8_t HwSetClock(SdmmcDriver *drv, uint32_t * pIoValClk);
extern uint8_t HwPowerDevice(SdmmcDriver *drv, uint8_t nowSwitchOn);
extern bool HwIsTimingSupported(SdmmcDriver *drv, uint8_t timingMode);
extern uint32_t SdmmcGetMaxFreq(SdmmcDriver *drv);
extern uint8_t SdMmcSelect(SdmmcDriver *drv, uint16_t address, uint8_t statCheck);
extern uint8_t SdMmcIdentify(SdmmcDriver *drv);
extern uint8_t SDMMC_Lib_SdStart(SdmmcDriver *drv, bool * retry);
extern void SdMmcUpdateInformation(SdmmcDriver *drv, bool csd, bool extData);
#endif /* CH_SDMMC_DEVICE_H_ */

View File

@ -0,0 +1,29 @@
#ifndef CH_SDMMC_MACROS_H_
#define CH_SDMMC_MACROS_H_
#define CACHE_ALIGNED __attribute__((aligned(L1_CACHE_BYTES)))
#define IS_CACHE_ALIGNED(x) ((((uint32_t)(x)) & (L1_CACHE_BYTES - 1)) == 0)
#define ROUND_INT_DIV(n,d) (((n) + ((d)-1)) / (d))
#define ROUND_UP_MULT(x,m) (((x) + ((m)-1)) & ~((m)-1))
#define CEIL_INT_DIV(n,d) (((n) + (d) - 1) / (d))
#define ABS_DIFF(a,b) ((a) < (b) ? (b) - (a) : (a) - (b))
#define ARRAY_SIZE(x) (sizeof ((x)) / sizeof(*(x)))
#define _PrintTitle(s) TRACE(s)
#define _PrintField(f,v) TRACE_1(f,v)
static inline uint32_t max_u32(uint32_t a, uint32_t b)
{
return a > b ? a : b;
}
static inline uint32_t min_u32(uint32_t a, uint32_t b)
{
return a < b ? a : b;
}
#endif /* CH_SDMMC_MACROS_H_ */

View File

@ -0,0 +1,375 @@
#include <string.h>
#include "hal.h"
#include "sama_sdmmc_lld.h"
#include "ch_sdmmc_device.h"
#include "ch_sdmmc_cmds.h"
#include "ch_sdmmc_sd.h"
#ifndef SDMMC_TRIM_MMC
/** Check if MMC Spec version 4 */
#define MMC_IsVer4(pSd) ( MMC_CSD_SPEC_VERS(pSd->CSD) >= 4 )
/** Check if MMC CSD structure is 1.2 (3.1 or later) */
#define MMC_IsCSDVer1_2(pSd) \
( (SD_CSD_STRUCTURE(pSd->CSD)==2) \
||(SD_CSD_STRUCTURE(pSd->CSD)>2&&MMC_EXT_CSD_STRUCTURE(pSd->EXT)>=2) )
/** MMC transfer multiplier factor codes (1/10) list */
const uint8_t mmcTransMultipliers[16] = {
0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
};
static uint8_t mmcSelectBuswidth(SdmmcDriver *driver, uint8_t busWidth, bool ddr)
{
uint8_t error;
uint32_t status;
MmcCmd6Arg cmd6Arg = {
.access = 0x3, /* Write byte in the EXT_CSD register */
.index = MMC_EXT_BUS_WIDTH_I, /* Target byte in EXT_CSD */
.value = MMC_EXT_BUS_WIDTH_1BIT, /* Byte value */
};
if (busWidth == 8)
cmd6Arg.value = MMC_EXT_BUS_WIDTH_8BITS
| (ddr ? MMC_EXT_BUS_WIDTH_DDR : 0);
else if (busWidth == 4)
cmd6Arg.value = MMC_EXT_BUS_WIDTH_4BITS
| (ddr ? MMC_EXT_BUS_WIDTH_DDR : 0);
else if (busWidth != 1)
return SDMMC_PARAM;
error = MmcCmd6(driver, &cmd6Arg, &status);
if (error)
return SDMMC_ERR;
else if (status & STATUS_MMC_SWITCH)
return SDMMC_NOT_SUPPORTED;
return SDMMC_OK;
}
static uint8_t mmcDetectBuswidth(SdmmcDriver *driver)
{
uint8_t error, busWidth, mask = 0xff, i, len;
sSdCard *pSd =&driver->card;
//assert(sizeof(pSd->sandbox1) >= 8);
//assert(sizeof(pSd->sandbox2) >= 8);
memset(pSd->sandbox1, 0, 8);
for (busWidth = 8; busWidth != 0; busWidth /= busWidth == 8 ? 2 : 4) {
error = HwSetBusWidth(driver, busWidth);
if (error)
continue;
switch (busWidth) {
case 8:
pSd->sandbox1[0] = 0x55;
pSd->sandbox1[1] = 0xaa;
break;
case 4:
pSd->sandbox1[0] = 0x5a;
pSd->sandbox1[1] = 0;
break;
case 1:
pSd->sandbox1[0] = 0x80;
pSd->sandbox1[1] = 0;
break;
}
len = (uint8_t)max_u32(busWidth, 2);
error = Cmd19(driver, pSd->sandbox1, len, NULL);
if (error) {
/* Devices which do not respond to CMD19 - which results
* in the driver returning SDMMC_ERROR_NORESPONSE -
* simply do not support the bus test procedure.
* When the device responds to CMD19, mind the
* difference with other data write commands: further
* to host data, the device does not emit the CRC status
* token. Typically the peripheral reports the anomaly,
* and the driver is likely to return SDMMC_ERR_IO. */
if (error != SDMMC_ERR_IO)
return 0;
}
error = Cmd14(driver, pSd->sandbox2, busWidth, NULL);
if (error)
continue;
if (busWidth == 1) {
mask = 0xc0;
pSd->sandbox2[0] &= mask;
}
len = busWidth == 8 ? 2 : 1;
for (i = 0; i < len; i++) {
if ((pSd->sandbox1[i] ^ pSd->sandbox2[i]) != mask)
break;
}
if (i == len)
break;
}
return busWidth;
}
uint8_t MmcGetExtInformation(SdmmcDriver *driver)
{
sSdCard *pSd = &driver->card;
/* MMC 4.0 Higher version */
if (SD_CSD_STRUCTURE(pSd->CSD) >= 2 && MMC_IsVer4(pSd))
return MmcCmd8(driver);
else
return SDMMC_NOT_SUPPORTED;
}
uint8_t MmcInit(SdmmcDriver *driver)
{
MmcCmd6Arg sw_arg = {
.access = 0x3, /* Write byte in the EXT_CSD register */
};
uint64_t mem_size;
uint32_t freq, drv_err, status;
uint8_t error, tim_mode, pwr_class, width;
bool flag;
tim_mode = driver->card.bSpeedMode = SDMMC_TIM_MMC_BC;
/* The host then issues the command ALL_SEND_CID (CMD2) to the card to get
* its unique card identification (CID) number.
* Card that is unidentified (i.e. which is in Ready State) sends its CID
* number as the response (on the CMD line). */
error = Cmd2(driver);
if (error)
return error;
/* Thereafter, the host issues SET_RELATIVE_ADDR (CMD3) to assign the
* device a dedicated relative card address (RCA), which is shorter than
* CID and which is used to address the card in the future data transfer
* mode. Once the RCA is received the card state changes to the Stand-by
* State. */
error = Cmd3(driver);
if (error)
return error;
//else
TRACE_1("RCA=%u\n\r", driver->card.wAddress);
/* SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register),
* e.g. block length, card storage capacity, etc... */
error = Cmd9(driver);
if (error)
return error;
/* Calculate transfer speed */
freq = SdmmcGetMaxFreq(driver);
error = HwSetClock(driver, &freq);
if (error != SDMMC_OK && error != SDMMC_CHANGED)
return error;
driver->card.dwCurrSpeed = freq;
/* Now select the card, to TRAN state */
error = SdMmcSelect(driver, driver->card.wAddress, 0);
if (error)
return error;
/* If CSD:SPEC_VERS indicates v4.0 or higher, read EXT_CSD */
error = MmcGetExtInformation(driver);
/* Consider HS200 timing mode */
if (error == SDMMC_OK && MMC_EXT_EXT_CSD_REV(driver->card.EXT) >= 6
&& MMC_IsCSDVer1_2((&driver->card)) && MMC_EXT_CARD_TYPE(driver->card.EXT) & 0x10
&& HwIsTimingSupported(driver, SDMMC_TIM_MMC_HS200))
tim_mode = SDMMC_TIM_MMC_HS200;
/* Consider High Speed DDR timing mode */
else if (error == SDMMC_OK && MMC_EXT_EXT_CSD_REV(driver->card.EXT) >= 4
&& MMC_IsCSDVer1_2((&driver->card)) && MMC_EXT_CARD_TYPE(driver->card.EXT) & 0x4
&& HwIsTimingSupported(driver, SDMMC_TIM_MMC_HS_DDR))
tim_mode = SDMMC_TIM_MMC_HS_DDR;
/* Consider High Speed SDR timing mode */
else if (error == SDMMC_OK
&& MMC_IsCSDVer1_2((&driver->card)) && MMC_EXT_CARD_TYPE(driver->card.EXT) & 0x1
&& HwIsTimingSupported(driver, SDMMC_TIM_MMC_HS_SDR))
tim_mode = SDMMC_TIM_MMC_HS_SDR;
/* Check power requirements of the device */
if (error == SDMMC_OK) {
if (tim_mode == SDMMC_TIM_MMC_HS200)
pwr_class = MMC_EXT_PWR_CL_200_195(driver->card.EXT);
else if (tim_mode == SDMMC_TIM_MMC_HS_DDR)
pwr_class = MMC_EXT_PWR_CL_DDR_52_360(driver->card.EXT);
else if (tim_mode == SDMMC_TIM_MMC_HS_SDR)
pwr_class = MMC_EXT_PWR_CL_52_360(driver->card.EXT);
else
pwr_class = MMC_EXT_PWR_CL_26_360(driver->card.EXT);
if (pwr_class != 0) {
sw_arg.index = MMC_EXT_POWER_CLASS_I;
sw_arg.value = 0xf;
error = MmcCmd6(driver, &sw_arg, &status);
if (error) {
TRACE_1("Pwr class %s\n\r",
SD_StringifyRetCode(error));
}
}
}
/* Enable High Speed SDR timing mode */
if (tim_mode == SDMMC_TIM_MMC_HS_SDR || tim_mode == SDMMC_TIM_MMC_HS_DDR) {
sw_arg.index = MMC_EXT_HS_TIMING_I;
sw_arg.value = MMC_EXT_HS_TIMING_EN;
error = MmcCmd6(driver, &sw_arg, &status);
if (error == SDMMC_OK)
error = HwSetHsMode(driver, SDMMC_TIM_MMC_HS_SDR);
if (error == SDMMC_OK)
error = Cmd13(driver, &status);
if (error == SDMMC_OK && (status & ~STATUS_STATE
& ~STATUS_READY_FOR_DATA
|| (status & STATUS_STATE) != STATUS_TRAN))
error = SDMMC_STATE;
if (error == SDMMC_OK) {
driver->card.bSpeedMode = SDMMC_TIM_MMC_HS_SDR;
freq = SdmmcGetMaxFreq(driver);
error = HwSetClock(driver, &freq);
driver->card.dwCurrSpeed = freq;
error = error == SDMMC_CHANGED ? SDMMC_OK : error;
}
if (error != SDMMC_OK) {
TRACE_1("HS %s\n\r", SD_StringifyRetCode(error));
return error;
}
}
/* Consider using the widest supported data bus */
if (MMC_IsCSDVer1_2((&driver->card)) && MMC_IsVer4((&driver->card))) {
width = mmcDetectBuswidth(driver);
if (width > 1) {
error = mmcSelectBuswidth(driver, width,tim_mode == SDMMC_TIM_MMC_HS_DDR);
if (error == SDMMC_OK)
error = HwSetBusWidth(driver, width);
if (error == SDMMC_OK)
driver->card.bBusMode = width;
if (error == SDMMC_OK && tim_mode == SDMMC_TIM_MMC_HS_DDR)
/* Switch to High Speed DDR timing mode */
error = HwSetHsMode(driver, tim_mode);
if (error == SDMMC_OK)
error = Cmd13(driver, &status);
if (error == SDMMC_OK && (status & ~STATUS_STATE
& ~STATUS_READY_FOR_DATA
|| (status & STATUS_STATE) != STATUS_TRAN))
error = SDMMC_STATE;
if (error == SDMMC_OK
&& tim_mode == SDMMC_TIM_MMC_HS_DDR)
driver->card.bSpeedMode = tim_mode;
else if (error) {
TRACE_1("Width/DDR %s\n\r",
SD_StringifyRetCode(error));
return error;
}
}
}
/* Enable HS200 timing mode */
if (tim_mode == SDMMC_TIM_MMC_HS200 && driver->card.bBusMode > 1) {
sw_arg.index = MMC_EXT_HS_TIMING_I;
/* Select device output Driver Type-0, i.e. 50 ohm nominal
* output impedance.
* TODO select the optimal device output Driver Type.
* That depends on board design. Use an oscilloscope to observe
* signal integrity, and among the driver types that meet rise
* and fall time requirements, select the weakest. */
sw_arg.value = MMC_EXT_HS_TIMING_HS200 | MMC_EXT_HS_TIMING_50R;
error = MmcCmd6(driver, &sw_arg, &status);
if (error == SDMMC_OK)
error = HwSetHsMode(driver, tim_mode);
if (error == SDMMC_OK) {
error = Cmd13(driver, &status);
if (error == SDMMC_OK && (status & ~STATUS_STATE
& ~STATUS_READY_FOR_DATA
|| (status & STATUS_STATE) != STATUS_TRAN))
error = SDMMC_STATE;
}
if (error == SDMMC_OK) {
driver->card.bSpeedMode = tim_mode;
freq = SdmmcGetMaxFreq(driver);
error = HwSetClock(driver, &freq);
driver->card.dwCurrSpeed = freq;
error = error == SDMMC_CHANGED ? SDMMC_OK : error;
}
if (error != SDMMC_OK) {
TRACE_1("HS200 %s\n\r", SD_StringifyRetCode(error));
return error;
}
}
/* Update card information since status changed */
flag = driver->card.bSpeedMode >= SDMMC_TIM_MMC_HS_SDR;
if (flag || driver->card.bBusMode > 1)
SdMmcUpdateInformation(driver, flag, true);
/* MMC devices have the SET_BLOCK_COUNT command part of both the
* block-oriented read and the block-oriented write commands,
* i.e. class 2 and class 4 commands.
* FIXME we should normally check CSD.CCC before issuing any MMC block-
* oriented read/write command. */
driver->card.bSetBlkCnt = 1;
/* Ask the driver to implicitly send the SET_BLOCK_COUNT command,
* immediately before every READ_MULTIPLE_BLOCK and WRITE_MULTIPLE_BLOCK
* command. */
driver->control_param = driver->card.bSetBlkCnt;
drv_err = sdmmc_device_control(driver,SDMMC_IOCTL_SET_LENPREFIX);
/* In case the driver does not support this function, we'll take it in
* charge. */
if (driver->card.bSetBlkCnt && drv_err == SDMMC_OK && driver->control_param)
driver->card.bSetBlkCnt = 0;
driver->card.wCurrBlockLen = SDMMC_BLOCK_SIZE;
if (MMC_IsCSDVer1_2((&driver->card)) && MMC_IsVer4((&driver->card))) {
/* Get size from EXT_CSD */
if (MMC_EXT_DATA_SECTOR_SIZE(driver->card.EXT)
== MMC_EXT_DATA_SECT_4KIB)
driver->card.wBlockSize = 4096;
else
driver->card.wBlockSize = 512;
driver->card.dwNbBlocks = MMC_EXT_SEC_COUNT(driver->card.EXT)
/ (driver->card.wBlockSize / 512UL);
/* Device density >= 4 GiB does not fit 32-bit dwTotalSize */
driver->card.dwTotalSize = MMC_EXT_SEC_COUNT(driver->card.EXT);
if (driver->card.dwTotalSize >= 0x800000)
driver->card.dwTotalSize = 0xFFFFFFFF;
else
driver->card.dwTotalSize *= 512UL;
}
else {
driver->card.wBlockSize = 512;
mem_size = SD_CSD_TOTAL_SIZE(driver->card.CSD);
driver->card.dwNbBlocks = (uint32_t)(mem_size >> 9);
driver->card.dwTotalSize = mem_size >> 32 ? 0xFFFFFFFF
: (uint32_t)mem_size;
}
/* Check device status and eat past exceptions, which would otherwise
* prevent upcoming data transaction routines from reliably checking
* fresh exceptions. */
error = Cmd13(driver, &status);
if (error)
return error;
status = status & ~STATUS_STATE & ~STATUS_READY_FOR_DATA & ~STATUS_APP_CMD;
if (status) {
TRACE_1("st %lx\n\r", status);
}
return SDMMC_OK;
}
#endif

View File

@ -0,0 +1,8 @@
#ifndef CH_SDMMC_MMC_H_
#define CH_SDMMC_MMC_H_
extern uint8_t MmcInit(SdmmcDriver *driver);
extern uint8_t MmcGetExtInformation(SdmmcDriver *driver);
#endif /* CH_SDMMC_MMC_H_ */

View File

@ -0,0 +1,830 @@
#include "hal.h"
#include "sama_sdmmc_lld.h"
#include "ch_sdmmc_pmc.h"
struct _pmc_main_osc {
uint32_t rc_freq;
uint32_t crystal_freq;
};
enum _slowclock_domain {
SLOWCLOCK_DOMAIN_DEFAULT, /* Default slow clock, used as input for peripherals */
#ifdef CONFIG_HAVE_SLOWCLOCK_TIMING_DOMAIN
SLOWCLOCK_DOMAIN_TIMING, /* Timing Domain slow clock (RTC, RTT) */
#endif
};
static uint32_t _pmc_mck = 0;
static struct _pmc_main_osc _pmc_main_oscillators = {
.rc_freq = MAIN_CLOCK_INT_OSC,
};
uint32_t pmc_get_slow_clock(void);
void pmc_disable_gck(uint32_t id);
int pmc_select_external_osc(bool bypass);
void pmc_enable_internal_osc(void);
void pmc_switch_mck_to_slck(void);
void pmc_select_internal_osc(void);
void pmc_disable_external_osc(void);
void pmc_disable_internal_osc(void);
static uint16_t _pmc_measure_main_osc_freq(bool external_xt)
{
volatile uint32_t timeout = MAINFRDY_TIMEOUT;
#ifdef CKGR_MCFR_CCSS
PMC->CKGR_MCFR = external_xt ? CKGR_MCFR_CCSS : 0;
#endif
#ifdef CKGR_MCFR_RCMEAS
PMC->CKGR_MCFR |= CKGR_MCFR_RCMEAS;
#endif
timeout = MAINFRDY_TIMEOUT;
while (!(PMC->CKGR_MCFR & CKGR_MCFR_MAINFRDY) && --timeout > 0);
return (timeout ?
((PMC->CKGR_MCFR & CKGR_MCFR_MAINF_Msk) >> CKGR_MCFR_MAINF_Pos) :
0u);
}
void pmc_switch_mck_to_new_source(uint32_t mckr_css)
{
uint32_t mckr = PMC->PMC_MCKR;
uint32_t mask = PMC_MCKR_CSS_Msk;
if ((mckr ^ mckr_css) & mask) {
PMC->PMC_MCKR = (mckr & ~mask) | (mckr_css & mask);
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
_pmc_mck = 0;
}
uint32_t pmc_set_main_oscillator_freq(uint32_t freq)
{
uint32_t mor, mckr, mckr_mask;
uint16_t mainf_rc, mainf_xt = 0;
_pmc_main_oscillators.crystal_freq = freq;
if (freq > 0)
return freq;
#if 1
/*
* Save the current value of the CKGR_MCKR register then swith to
* the slow clock.
*/
mckr = PMC->PMC_MCKR;
pmc_switch_mck_to_slck();
mckr_mask = PMC_MCKR_MDIV_Msk | PMC_MCKR_PRES_Msk;
PMC->PMC_MCKR &= ~mckr_mask;
/* Save the current value of the CKGR_MOR register. */
mor = PMC->CKGR_MOR;
/* Measure the 12MHz RC frequency. */
pmc_select_internal_osc();
mainf_rc = _pmc_measure_main_osc_freq(false);
/* Measure the crystal or by-pass frequency. */
/* Try by-pass first. */
if (pmc_select_external_osc(true) == 0)
mainf_xt = _pmc_measure_main_osc_freq(true);
/* Then try external crytal if no by-pass. */
if (!mainf_xt) {
if (pmc_select_external_osc(false) == 0)
mainf_xt = _pmc_measure_main_osc_freq(true);
}
/* Switch back to internal 12MHz RC if it was selected initially */
if (!(mor & CKGR_MOR_MOSCSEL))
pmc_select_internal_osc();
#ifdef CKGR_MOR_MOSCRCEN
/* Disable internal oscillator if it wasn't enabled initially */
if (!(mor & CKGR_MOR_MOSCRCEN))
pmc_disable_internal_osc();
#endif
/* Switch back to the former MCK source. */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~mckr_mask) | (mckr & mckr_mask);
pmc_switch_mck_to_new_source(mckr & PMC_MCKR_CSS_Msk);
/* Guess the external crystal frequency, if available. */
if (mainf_rc && mainf_xt) {
uint32_t ratio = (mainf_xt * 1000) / mainf_rc;
// Use 10% low and high margins
if (1800 <= ratio && ratio <= 2200) {
// 24/12 => ratio = 2000
_pmc_main_oscillators.crystal_freq = 24000000u;
} else if (1200 <= ratio && ratio <= 1467) {
// 16/12 => ratio = 1333
_pmc_main_oscillators.crystal_freq = 16000000u;
} else if (900 <= ratio && ratio <= 1100) {
// 12/12 => ratio = 1000
_pmc_main_oscillators.crystal_freq = 12000000u;
} else if (600 <= ratio && ratio <= 733) {
// 8/12 => ratio = 667
_pmc_main_oscillators.crystal_freq = 8000000u;
}
}
#endif
return _pmc_main_oscillators.crystal_freq;
}
bool pmc_is_peripheral_enabled(uint32_t id)
{
// assert(id < ID_PERIPH_COUNT);
#ifdef PMC_CSR_PID0
return (PMC->PMC_CSR[(id >> 5) & 3] & (1 << (id & 31))) != 0;
#else
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
return (pcr & PMC_PCR_EN) != 0;
#endif
}
void pmc_enable_peripheral(uint32_t id)
{
osalDbgCheck(id < ID_PERIPH_COUNT);
// select peripheral
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = pcr | PMC_PCR_CMD | PMC_PCR_EN;
}
void pmc_disable_peripheral(uint32_t id)
{
osalDbgCheck(id < ID_PERIPH_COUNT);
// select peripheral
PMC->PMC_PCR = PMC_PCR_PID(id);
// disable it but keep previous configuration
PMC->PMC_PCR = (PMC->PMC_PCR & ~PMC_PCR_EN) | PMC_PCR_CMD;
}
Matrix* get_peripheral_matrix(uint32_t id)
{
//int i;
switch(id)
{
case ID_ARM_PMU: /* 2: Performance Monitor Unit (PMU) (ARM_PMU) */
case ID_XDMAC0: /* 6: DMA Controller 0 (XDMAC0) */
case ID_XDMAC1: /* 7: DMA Controller 1 (XDMAC1) */
case ID_AES: /* 9: Advanced Enion Standard (AES) */
case ID_AESB: /* 10: AES bridge (AESB) */
case ID_SHA: /* 12: SHA Signature (SHA) */
case ID_MPDDRC: /* 13: MPDDR controller (MPDDRC) */
case ID_MATRIX0: /* 15: H64MX: 64-bit AHB Matrix (MATRIX0) */
case ID_SDMMC0: /* 31: Secure Digital Multimedia Card Controller 0 (SDMMC0) */
case ID_SDMMC1: /* 32: Secure Digital Multimedia Card Controller 1 (SDMMC1) */
case ID_LCDC: /* 45: LCD Controller (LCDC) */
case ID_ISC: /* 46: Camera Interface (ISC) */
case ID_QSPI0: /* 52: QSPI 0 (QSPI0) */
case ID_QSPI1: /* 53: QSPI 1 (QSPI1) */
case ID_L2CC: /* 63: L2 Cache Controller (L2CC) */
return MATRIX0; // AHB 64-bit matrix
default:
return MATRIX1; // AHB 32-bit matrix
};
}
uint32_t get_peripheral_clock_matrix_div(uint32_t id)
{
Matrix* matrix = get_peripheral_matrix(id);
if (matrix == MATRIX1) {
if (PMC->PMC_MCKR & PMC_MCKR_H32MXDIV_H32MXDIV2)
return 2;
else
return 1;
}
return 1;
}
uint32_t pmc_get_peripheral_clock(uint32_t id)
{
osalDbgCheck(id < ID_PERIPH_COUNT);
uint32_t div = get_peripheral_clock_matrix_div(id);
#ifdef CONFIG_HAVE_PMC_PERIPH_DIV
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
div *= 1 << ((pcr & PMC_PCR_DIV_Msk) >> PMC_PCR_DIV_Pos);
#endif
return pmc_get_master_clock() / div;
}
bool slowclock_is_internal(enum _slowclock_domain domain)
{
(void)domain;
return (SCKC->SCKC_CR & SCKC_CR_OSCSEL) != SCKC_CR_OSCSEL;
}
uint32_t slowclock_get_clock(enum _slowclock_domain domain)
{
if (slowclock_is_internal(domain))
return 32000;
else
return 32768;
}
uint32_t pmc_get_slow_clock(void)
{
return slowclock_get_clock(SLOWCLOCK_DOMAIN_DEFAULT);
}
uint32_t pmc_get_upll_clock(void)
{
uint32_t upllclk;
#if defined(SFR_UTMICKTRIM_FREQ_Msk)
uint32_t clktrim = SFR->SFR_UTMICKTRIM & SFR_UTMICKTRIM_FREQ_Msk;
switch (clktrim) {
#ifdef SFR_UTMICKTRIM_FREQ_48
case SFR_UTMICKTRIM_FREQ_48:
upllclk = 10 * _pmc_main_oscillators.crystal_freq;
break;
#endif
case SFR_UTMICKTRIM_FREQ_24:
upllclk = 20 * _pmc_main_oscillators.crystal_freq;
break;
case SFR_UTMICKTRIM_FREQ_16:
upllclk = 30 * _pmc_main_oscillators.crystal_freq;
break;
default:
upllclk = 40 * _pmc_main_oscillators.crystal_freq;
break;
}
#elif defined(UTMI_CKTRIM_FREQ_Msk)
uint32_t clktrim = UTMI->UTMI_CKTRIM & UTMI_CKTRIM_FREQ_Msk;
switch (clktrim) {
case UTMI_CKTRIM_FREQ_XTAL16:
upllclk = 30 * _pmc_main_oscillators.crystal_freq;
break;
default:
upllclk = 40 * _pmc_main_oscillators.crystal_freq;
break;
}
#else
upllclk = 40 * _pmc_main_oscillators.crystal_freq;
#endif
#ifdef CONFIG_HAVE_PMC_UPLLDIV2
if (PMC->PMC_MCKR & PMC_MCKR_UPLLDIV2)
upllclk >>= 1;
#endif
return upllclk;
}
uint32_t pmc_get_main_clock(void)
{
if (PMC->CKGR_MOR & CKGR_MOR_MOSCSEL)
return _pmc_main_oscillators.crystal_freq; /* external crystal */
else
return _pmc_main_oscillators.rc_freq; /* on-chip main clock RC */
}
uint32_t pmc_get_plla_clock(void)
{
uint32_t pllaclk, pllar, pllmula, plldiva;
pllar = PMC->CKGR_PLLAR;
pllmula = (pllar & CKGR_PLLAR_MULA_Msk) >> CKGR_PLLAR_MULA_Pos;
plldiva = (pllar & CKGR_PLLAR_DIVA_Msk) >> CKGR_PLLAR_DIVA_Pos;
if (plldiva == 0 || pllmula == 0)
return 0;
pllaclk = pmc_get_main_clock();
pllaclk = pllaclk * (pllmula + 1) / plldiva;
#ifdef CONFIG_HAVE_PMC_PLLADIV2
if (PMC->PMC_MCKR & PMC_MCKR_PLLADIV2)
pllaclk >>= 1;
#endif
return pllaclk;
}
uint32_t pmc_get_processor_clock(void)
{
uint32_t procclk, mdiv;
procclk = pmc_get_master_clock();
mdiv = PMC->PMC_MCKR & PMC_MCKR_MDIV_Msk;
switch (mdiv) {
case PMC_MCKR_MDIV_EQ_PCK:
break;
case PMC_MCKR_MDIV_PCK_DIV2:
procclk <<= 1; // multiply by 2
break;
case PMC_MCKR_MDIV_PCK_DIV3:
procclk *= 3; // multiply by 3
break;
case PMC_MCKR_MDIV_PCK_DIV4:
procclk <<= 2; // multiply by 4
break;
default:
/* should never get here... */
break;
}
return procclk;
}
static void _pmc_compute_mck(void)
{
uint32_t clk = 0;
uint32_t mckr = PMC->PMC_MCKR;
uint32_t css = mckr & PMC_MCKR_CSS_Msk;
switch (css) {
case PMC_MCKR_CSS_SLOW_CLK:
clk = pmc_get_slow_clock();
break;
case PMC_MCKR_CSS_MAIN_CLK:
clk = pmc_get_main_clock();
break;
case PMC_MCKR_CSS_PLLA_CLK:
clk = pmc_get_plla_clock();
break;
case PMC_MCKR_CSS_UPLL_CLK:
clk = pmc_get_upll_clock();
break;
default:
/* should never get here... */
break;
}
uint32_t pres = mckr & PMC_MCKR_PRES_Msk;
switch (pres) {
case PMC_MCKR_PRES_CLOCK:
break;
case PMC_MCKR_PRES_CLOCK_DIV2:
clk >>= 1;
break;
case PMC_MCKR_PRES_CLOCK_DIV4:
clk >>= 2;
break;
case PMC_MCKR_PRES_CLOCK_DIV8:
clk >>= 3;
break;
case PMC_MCKR_PRES_CLOCK_DIV16:
clk >>= 4;
break;
case PMC_MCKR_PRES_CLOCK_DIV32:
clk >>= 5;
break;
case PMC_MCKR_PRES_CLOCK_DIV64:
clk >>= 6;
break;
#ifdef PMC_MCKR_PRES_CLOCK_DIV3
case PMC_MCKR_PRES_CLOCK_DIV3:
clk /= 3;
break;
#endif
default:
/* should never get here... */
break;
}
uint32_t mdiv = mckr & PMC_MCKR_MDIV_Msk;
switch (mdiv) {
case PMC_MCKR_MDIV_EQ_PCK:
break;
case PMC_MCKR_MDIV_PCK_DIV2:
clk >>= 1; // divide by 2
break;
case PMC_MCKR_MDIV_PCK_DIV4:
clk >>= 2; // divide by 4
break;
case PMC_MCKR_MDIV_PCK_DIV3:
clk /= 3; // divide by 3
break;
default:
/* should never get here... */
break;
}
_pmc_mck = clk;
}
uint32_t pmc_get_master_clock(void)
{
if (!_pmc_mck)
_pmc_compute_mck();
return _pmc_mck;
}
void pmc_configure_gck(uint32_t id, uint32_t clock_source, uint32_t div)
{
osalDbgCheck(id < ID_PERIPH_COUNT);
osalDbgCheck(!(clock_source & ~PMC_PCR_GCKCSS_Msk));
osalDbgCheck(div > 0);
osalDbgCheck(!((div << PMC_PCR_GCKDIV_Pos) & ~PMC_PCR_GCKDIV_Msk));
pmc_disable_gck(id);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR & ~(PMC_PCR_GCKCSS_Msk | PMC_PCR_GCKDIV_Msk);
PMC->PMC_PCR = pcr | clock_source | PMC_PCR_CMD | PMC_PCR_GCKDIV(div - 1);
}
void pmc_enable_gck(uint32_t id)
{
osalDbgCheck(id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = pcr | PMC_PCR_CMD | PMC_PCR_GCKEN;
#ifdef PMC_GCSR_PID0
while ((PMC->PMC_GCSR[(id >> 5) & 3] & (1 << (id & 31))) == 0);
#else
while (!(PMC->PMC_SR & PMC_SR_GCKRDY));
#endif
}
void pmc_disable_gck(uint32_t id)
{
osalDbgCheck(id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = PMC_PCR_CMD | (pcr & ~PMC_PCR_GCKEN);
}
uint32_t pmc_get_gck_clock(uint32_t id)
{
uint32_t clk = 0;
osalDbgCheck(id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
switch (pcr & PMC_PCR_GCKCSS_Msk) {
case PMC_PCR_GCKCSS_SLOW_CLK:
clk = pmc_get_slow_clock();
break;
case PMC_PCR_GCKCSS_MAIN_CLK:
clk = pmc_get_main_clock();
break;
case PMC_PCR_GCKCSS_PLLA_CLK:
clk = pmc_get_plla_clock();
break;
case PMC_PCR_GCKCSS_UPLL_CLK:
clk = pmc_get_upll_clock();
break;
case PMC_PCR_GCKCSS_MCK_CLK:
clk = pmc_get_master_clock();
break;
#ifdef CONFIG_HAVE_PMC_AUDIO_CLOCK
case PMC_PCR_GCKCSS_AUDIO_CLK:
clk = pmc_get_audio_pmc_clock();
break;
#endif
}
uint32_t div = (pcr & PMC_PCR_GCKDIV_Msk) >> PMC_PCR_GCKDIV_Pos;
return ROUND_INT_DIV(clk, div + 1);
}
void pmc_configure_peripheral(uint32_t id, const struct _pmc_periph_cfg* cfg, bool enable)
{
osalDbgCheck(id < ID_PERIPH_COUNT);
pmc_disable_peripheral(id);
if (cfg != NULL) {
if (cfg->gck.div > 0)
pmc_configure_gck(id, cfg->gck.css, cfg->gck.div);
} else {
pmc_disable_gck(id);
}
/* Enable peripheral, gck or only configure it */
if (enable) {
if (cfg && cfg->gck.div > 0)
pmc_enable_gck(id);
pmc_enable_peripheral(id);
}
}
void pmc_enable_upll_clock(void)
{
uint32_t uckr = CKGR_UCKR_UPLLEN | CKGR_UCKR_UPLLCOUNT(0x3);
uckr |= CKGR_UCKR_BIASCOUNT(0x1);
#if defined(SFR_UTMICKTRIM_FREQ_Msk)
switch (_pmc_main_oscillators.crystal_freq) {
#ifdef SFR_UTMICKTRIM_FREQ_48
case 48000000:
SFR->SFR_UTMICKTRIM = (SFR->SFR_UTMICKTRIM & ~SFR_UTMICKTRIM_FREQ_Msk) | SFR_UTMICKTRIM_FREQ_48;
break;
#endif
case 24000000:
SFR->SFR_UTMICKTRIM = (SFR->SFR_UTMICKTRIM & ~SFR_UTMICKTRIM_FREQ_Msk) | SFR_UTMICKTRIM_FREQ_24;
break;
case 16000000:
SFR->SFR_UTMICKTRIM = (SFR->SFR_UTMICKTRIM & ~SFR_UTMICKTRIM_FREQ_Msk) | SFR_UTMICKTRIM_FREQ_16;
break;
default:
SFR->SFR_UTMICKTRIM = (SFR->SFR_UTMICKTRIM & ~SFR_UTMICKTRIM_FREQ_Msk) | SFR_UTMICKTRIM_FREQ_12;
}
#elif defined(UTMI_CKTRIM_FREQ_Msk)
switch (_pmc_main_oscillators.crystal_freq) {
case 16000000:
UTMI->UTMI_CKTRIM = (UTMI->UTMI_CKTRIM & ~UTMI_CKTRIM_FREQ_Msk) | UTMI_CKTRIM_FREQ_XTAL16;
break;
default:
UTMI->UTMI_CKTRIM = (UTMI->UTMI_CKTRIM & ~UTMI_CKTRIM_FREQ_Msk) | UTMI_CKTRIM_FREQ_XTAL12;
}
#endif
/* enable the 480MHz UTMI PLL */
PMC->CKGR_UCKR = uckr;
/* wait until UPLL is locked */
while (!(PMC->PMC_SR & PMC_SR_LOCKU));
}
void pmc_enable_upll_bias(void)
{
PMC->CKGR_UCKR |= CKGR_UCKR_BIASEN;
}
void pmc_disable_upll_bias(void)
{
PMC->CKGR_UCKR &= ~CKGR_UCKR_BIASEN;
}
void pmc_switch_mck_to_slck(void)
{
/* Select Slow Clock as input clock for PCK and MCK */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_SLOW_CLK;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
_pmc_mck = 0;
}
void pmc_select_internal_osc(void)
{
pmc_enable_internal_osc();
/* switch MAIN clock to internal 12MHz RC */
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCSEL | CKGR_MOR_KEY_Msk)) | CKGR_MOR_KEY_PASSWD;
/* in case where MCK is running on MAIN CLK */
if ((PMC->PMC_MCKR & PMC_MCKR_CSS_PLLA_CLK) || (PMC->PMC_MCKR & PMC_MCKR_CSS_MAIN_CLK))
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
/* disable external OSC 12 MHz to save power*/
pmc_disable_external_osc();
}
int pmc_enable_external_osc(bool bypass)
{
uint32_t cgmor = PMC->CKGR_MOR;
uint32_t mask = CKGR_MOR_MOSCXTEN;
volatile uint32_t timeout;
if (bypass)
mask = CKGR_MOR_MOSCXTBY;
/* Enable Crystal Oscillator if needed */
if ((cgmor & mask) != mask) {
cgmor &= ~CKGR_MOR_KEY_Msk;
cgmor |= CKGR_MOR_KEY_PASSWD;
if (bypass) {
/* Disable Crystal Oscillator */
cgmor &= ~CKGR_MOR_MOSCXTEN;
PMC->CKGR_MOR = cgmor;
/* Wait Main Oscillator not ready */
while (PMC->PMC_SR & PMC_SR_MOSCXTS);
/* Enable Crystal Oscillator Bypass */
cgmor |= CKGR_MOR_MOSCXTBY;
PMC->CKGR_MOR = cgmor;
} else {
/* Disable Crystal Oscillator Bypass */
cgmor &= ~CKGR_MOR_MOSCXTBY;
PMC->CKGR_MOR = cgmor;
/* Wait Main Oscillator not ready */
while (PMC->PMC_SR & PMC_SR_MOSCXTS);
/* Set Oscillator Startup Time */
cgmor &= ~CKGR_MOR_MOSCXTST_Msk;
cgmor |= CKGR_MOR_MOSCXTST(18);
PMC->CKGR_MOR = cgmor;
/* Enable Crystal Oscillator */
cgmor |= CKGR_MOR_MOSCXTEN;
PMC->CKGR_MOR = cgmor;
}
/* Wait Main Oscillator ready */
timeout = MOSCXTS_TIMEOUT;
while (!(PMC->PMC_SR & PMC_SR_MOSCXTS) && --timeout > 0);
/* Return true if oscillator ready before timeout */
return timeout == 0 ? -1 : 0;
} else {
/* Crystal Oscillator already selected, just check if ready */
if (PMC->PMC_SR & PMC_SR_MOSCXTS)
return 0;
else
return -2;
}
}
void pmc_disable_external_osc(void)
{
/* disable external OSC */
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCSEL | CKGR_MOR_KEY_Msk)) | CKGR_MOR_KEY_PASSWD;
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCXTBY | CKGR_MOR_KEY_Msk)) | CKGR_MOR_KEY_PASSWD;
}
int pmc_select_external_osc(bool bypass)
{
int err;
volatile uint32_t timeout;
/* Return if external oscillator had been selected */
if ((PMC->CKGR_MOR & CKGR_MOR_MOSCSEL) == CKGR_MOR_MOSCSEL) {
uint32_t mask = bypass ? CKGR_MOR_MOSCXTBY : CKGR_MOR_MOSCXTEN;
if ((PMC->CKGR_MOR & mask) == mask)
return 0;
}
/*
* When switching the source of the main clock between the RC oscillator and the crystal
* oscillator, both oscillators must be enabled. After completion of the switch, the
* unused oscillator can be disabled.
*/
pmc_enable_internal_osc();
err = pmc_enable_external_osc(bypass);
if (err < 0)
return err;
/* switch MAIN clock to external oscillator */
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_KEY_Msk) | CKGR_MOR_MOSCSEL
| CKGR_MOR_KEY_PASSWD;
/* wait for the command to be taken into account */
while ((PMC->CKGR_MOR & CKGR_MOR_MOSCSEL) != CKGR_MOR_MOSCSEL);
/* wait MAIN clock status change for external oscillator selection */
timeout = MOSCSELS_TIMEOUT;
while (!(PMC->PMC_SR & PMC_SR_MOSCSELS) && --timeout > 0);
if (!timeout) {
PMC->CKGR_MOR &= ~CKGR_MOR_MOSCSEL;
return -1;
}
/* in case where MCK is running on MAIN CLK */
if ((PMC->PMC_MCKR & PMC_MCKR_CSS_PLLA_CLK) || (PMC->PMC_MCKR & PMC_MCKR_CSS_MAIN_CLK))
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
/* disable internal 12MHz RC to save power */
pmc_disable_internal_osc();
return 0;
}
void pmc_enable_internal_osc(void)
{
#ifdef CKGR_MOR_MOSCRCEN
/* Enable internal 12MHz RC when needed */
if ((PMC->CKGR_MOR & CKGR_MOR_MOSCRCEN) != CKGR_MOR_MOSCRCEN) {
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_KEY_Msk) | CKGR_MOR_MOSCRCEN | CKGR_MOR_KEY_PASSWD;
/* Wait internal 12MHz RC Startup Time for clock stabilization */
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));
}
#endif
}
void pmc_switch_mck_to_pll(void)
{
/* Select PLL as input clock for PCK and MCK */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_PLLA_CLK;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
_pmc_mck = 0;
}
void pmc_set_mck_prescaler(uint32_t prescaler)
{
//assert(!(prescaler & ~PMC_MCKR_PRES_Msk));
/* Change MCK Prescaler divider in PMC_MCKR register */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | prescaler;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
#ifdef CONFIG_HAVE_PMC_H32MXDIV
void pmc_set_mck_h32mxdiv(bool div2)
{
uint32_t mckr = PMC->PMC_MCKR;
if (div2) {
if ((mckr & PMC_MCKR_H32MXDIV) != PMC_MCKR_H32MXDIV_H32MXDIV2)
PMC->PMC_MCKR = (mckr & ~PMC_MCKR_H32MXDIV) | PMC_MCKR_H32MXDIV_H32MXDIV2;
} else {
if ((mckr & PMC_MCKR_H32MXDIV) != PMC_MCKR_H32MXDIV_H32MXDIV1)
PMC->PMC_MCKR = (mckr & ~PMC_MCKR_H32MXDIV) | PMC_MCKR_H32MXDIV_H32MXDIV1;
}
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
#endif /* CONFIG_HAVE_PMC_H32MXDIV */
#ifdef CONFIG_HAVE_PMC_PLLADIV2
void pmc_set_mck_plladiv2(bool div2)
{
uint32_t mckr = PMC->PMC_MCKR;
if (div2) {
if ((mckr & PMC_MCKR_PLLADIV2) != PMC_MCKR_PLLADIV2)
PMC->PMC_MCKR = mckr | PMC_MCKR_PLLADIV2;
} else {
if ((mckr & PMC_MCKR_PLLADIV2) == PMC_MCKR_PLLADIV2)
PMC->PMC_MCKR = mckr & ~PMC_MCKR_PLLADIV2;
}
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
#endif
void pmc_set_mck_divider(uint32_t divider)
{
//assert(!(divider & ~PMC_MCKR_MDIV_Msk));
/* change MCK Prescaler divider in PMC_MCKR register */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | divider;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
void pmc_configure_plla(const struct _pmc_plla_cfg* plla)
{
uint32_t pllar = 0;
#ifdef CKGR_PLLAR_ONE
pllar |= CKGR_PLLAR_ONE;
#endif
pllar |= CKGR_PLLAR_MULA(plla->mul);
pllar |= CKGR_PLLAR_DIVA(plla->div);
pllar |= CKGR_PLLAR_PLLACOUNT(plla->count);
PMC->CKGR_PLLAR = pllar;
#ifdef CONFIG_HAVE_PMC_PLLA_CHARGEPUMP
PMC->PMC_PLLICPR = plla->icp & PMC_PLLICPR_ICP_PLLA_Msk;
#endif /* CONFIG_HAVE_PMC_PLLA_CHARGEPUMP */
if (plla->mul > 0)
while (!(PMC->PMC_SR & PMC_SR_LOCKA));
}
void pmc_disable_plla(void)
{
PMC->CKGR_PLLAR = (PMC->CKGR_PLLAR & ~CKGR_PLLAR_MULA_Msk) | CKGR_PLLAR_MULA(0);
}
void pmc_disable_internal_osc(void)
{
#ifdef CKGR_MOR_MOSCRCEN
/* disable internal 12MHz RC */
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN & ~CKGR_MOR_KEY_Msk) | CKGR_MOR_KEY_PASSWD;
#endif
}

View File

@ -0,0 +1,50 @@
#ifndef CH_SDMMC_PMC_H_
#define CH_SDMMC_PMC_H_
struct _pmc_plla_cfg {
/** PLLA MUL value */
uint32_t mul;
/** PLLA DIV value */
uint32_t div;
/** PLLA COUNT value (number of slow clock cycles before the PLLA is locked) */
uint32_t count;
#ifdef CONFIG_HAVE_PMC_PLLA_CHARGE_PUMP
/** PLLA ICP value */
uint32_t icp;
#endif
};
struct _pmc_periph_cfg{
struct {
/** gck source selection: SLOW, MAIN, PLLA, UPLL, MCK or AUDIO */
uint32_t css;
/** gck division ratio (0 means disable, n >= 1 divide by n) */
uint32_t div;
} gck;
};
#define pmcEnableSDMMC0() pmcEnablePidLow(ID_SDMMC0_MSK)
#define pmcDisableSDMMC0() pmcDisablePidLow(ID_SDMMC0_MSK)
#define pmcEnableSDMMC1() pmcEnablePidHigh(ID_SDMMC1_MSK)
#define pmcDisableSDMMC1() pmcDisablePidHigh(ID_SDMMC1_MSK)
extern void pmc_configure_peripheral(uint32_t id, const struct _pmc_periph_cfg* cfg, bool enable);
extern void pmc_enable_upll_clock(void);
extern void pmc_enable_upll_bias(void);
extern uint32_t pmc_get_peripheral_clock(uint32_t id);
extern uint32_t pmc_get_master_clock(void);
extern uint32_t pmc_get_gck_clock(uint32_t id);
extern bool pmc_is_peripheral_enabled(uint32_t id);
extern uint32_t pmc_set_main_oscillator_freq(uint32_t freq);
extern uint32_t pmc_get_slow_clock(void);
extern uint32_t pmc_get_processor_clock(void);
#endif /* CH_SDMMC_PMC_H_ */

View File

@ -0,0 +1,74 @@
#ifndef CH_SDMMC_SAMA5D2_H_
#define CH_SDMMC_SAMA5D2_H_
#define EXT_SIZE 512
#define SSR_SIZE 64
#define SCR_SIZE 8
#define SB1_SIZE 64
#define SB2_SIZE 8
/** Frequency of the board main clock oscillator */
#define BOARD_MAIN_CLOCK_EXT_OSC 12000000
#define MAIN_CLOCK_INT_OSC 12000000
#define OSC_STARTUP_TIME 0xFFu
#define MAINFRDY_TIMEOUT 32u
#define MOSCXTS_TIMEOUT ((OSC_STARTUP_TIME * 8) + 8)
#define MOSCSELS_TIMEOUT 32u
typedef enum
{
SDMMC_SLOT0 = 0,
SDMMC_SLOT1
}sdmmcslots_t;
#define CONFIG_HAVE_PMIC_ACT8945A 0
#define CONFIG_HAVE_PMC_PLLADIV2 1
#define CONFIG_HAVE_PMC_H32MXDIV 1
/* ========== Pio definition for SDMMC0 peripheral ========== */
#define PIO_PA13A_SDMMC0_CD (1u << 13) /**< \brief Sdmmc0 signal: SDMMC0_CD */
#define PIO_PA11A_SDMMC0_VDDSEL (1u << 11)/**< \brief Sdmmc0 signal: SDMMC0_VDDSEL */
#define PIO_PA10A_SDMMC0_RSTN (1u << 10) /**< \brief Sdmmc0 signal: SDMMC0_RSTN */
#define PIO_PA0A_SDMMC0_CK (1u << 0) /**< \brief Sdmmc0 signal: SDMMC0_CK */
#define PIO_PA1A_SDMMC0_CMD (1u << 1) /**< \brief Sdmmc0 signal: SDMMC0_CMD */
#define PIO_PA12A_SDMMC0_WP (1u << 12) /**< \brief Sdmmc0 signal: SDMMC0_WP */
#define PIO_PA2A_SDMMC0_DAT0 (1u << 2) /**< \brief Sdmmc0 signal: SDMMC0_DAT0 */
#define PIO_PA3A_SDMMC0_DAT1 (1u << 3) /**< \brief Sdmmc0 signal: SDMMC0_DAT1 */
#define PIO_PA4A_SDMMC0_DAT2 (1u << 4) /**< \brief Sdmmc0 signal: SDMMC0_DAT2 */
#define PIO_PA5A_SDMMC0_DAT3 (1u << 5) /**< \brief Sdmmc0 signal: SDMMC0_DAT3 */
#define PIO_PA6A_SDMMC0_DAT4 (1u << 6) /**< \brief Sdmmc0 signal: SDMMC0_DAT4 */
#define PIO_PA7A_SDMMC0_DAT5 (1u << 7) /**< \brief Sdmmc0 signal: SDMMC0_DAT5 */
#define PIO_PA8A_SDMMC0_DAT6 (1u << 8) /**< \brief Sdmmc0 signal: SDMMC0_DAT6 */
#define PIO_PA9A_SDMMC0_DAT7 (1u << 9) /**< \brief Sdmmc0 signal: SDMMC0_DAT7 */
/* ========== Pio PIN definition for SDMMC0 peripheral ========== */
/* ========== Pio definition for SDMMC1 peripheral ========== */
#define PIO_PA30E_SDMMC1_CD (1u << 30) /**< \brief Sdmmc1 signal: SDMMC1_CD */
#define PIO_PA27E_SDMMC1_RSTN (1u << 27) /**< \brief Sdmmc1 signal: SDMMC1_RSTN */
#define PIO_PA22E_SDMMC1_CK (1u << 22) /**< \brief Sdmmc1 signal: SDMMC1_CK */
#define PIO_PA28E_SDMMC1_CMD (1u << 28) /**< \brief Sdmmc1 signal: SDMMC1_CMD */
#define PIO_PA29E_SDMMC1_WP (1u << 29) /**< \brief Sdmmc1 signal: SDMMC1_WP */
#define PIO_PA18E_SDMMC1_DAT0 (1u << 18) /**< \brief Sdmmc1 signal: SDMMC1_DAT0 */
#define PIO_PA19E_SDMMC1_DAT1 (1u << 19) /**< \brief Sdmmc1 signal: SDMMC1_DAT1 */
#define PIO_PA20E_SDMMC1_DAT2 (1u << 20) /**< \brief Sdmmc1 signal: SDMMC1_DAT2 */
#define PIO_PA21E_SDMMC1_DAT3 (1u << 21) /**< \brief Sdmmc1 signal: SDMMC1_DAT3 */
/* mask for board capabilities defines: voltage, slot type and 8-bit support */
#define CAPS0_MASK (SDMMC_CA0R_V33VSUP | SDMMC_CA0R_V30VSUP | \
SDMMC_CA0R_V18VSUP | SDMMC_CA0R_SLTYPE_Msk | \
SDMMC_CA0R_ED8SUP)
#define BOARD_SDMMC0_CAPS0 (SDMMC_CA0R_V33VSUP | \
SDMMC_CA0R_V18VSUP | \
SDMMC_CA0R_SLTYPE_EMBEDDED | \
SDMMC_CA0R_ED8SUP)
#define BOARD_SDMMC1_CAPS0 (SDMMC_CA0R_V33VSUP | \
SDMMC_CA0R_SLTYPE_REMOVABLECARD)
#endif /* CH_SDMMC_SAMA5D2_H_ */

View File

@ -0,0 +1,781 @@
#include <string.h>
#include "hal.h"
#include "sama_sdmmc_lld.h"
#include "ch_sdmmc_device.h"
#include "ch_sdmmc_cmds.h"
#include "ch_sdmmc_sd.h"
#include "ch_sdmmc_sdio.h"
static uint8_t SdGetTimingFunction(uint8_t mode);
static void SdSelectSlowerTiming(bool high_sig, uint8_t * mode);
#if SAMA_SDMMC_TRACE == 1
struct stringEntry_s
{
const uint8_t key;
const char *name;
};
const char sdmmcInvalidCode[] = "!Invalid!";
const struct stringEntry_s sdmmcRCodeNames[] = {
{ SDMMC_OK, "OK", },
{ SDMMC_LOCKED, "ERR_LOCKED", },
{ SDMMC_BUSY, "ERR_BUSY", },
{ SDMMC_NO_RESPONSE, "ERR_NO_RESPONSE", },
{ SDMMC_CHANGED, "OK_CHANGED", },
{ SDMMC_ERR, "ERROR", },
{ SDMMC_ERR_IO, "ERR_IO", },
{ SDMMC_ERR_RESP, "ERR_RESP", },
{ SDMMC_NOT_INITIALIZED, "ERR_NOT_INITIALIZED", },
{ SDMMC_PARAM, "ERR_PARAM", },
{ SDMMC_STATE, "ERR_STATE", },
{ SDMMC_USER_CANCEL, "ERR_USER_CANCEL", },
{ SDMMC_NOT_SUPPORTED, "ERR_NO_SUPPORT", },
};
const struct stringEntry_s sdmmcIOCtrlNames[] = {
{ SDMMC_IOCTL_BUSY_CHECK, "BUSY_CHECK", },
{ SDMMC_IOCTL_POWER, "POWER", },
{ SDMMC_IOCTL_CANCEL_CMD, "CANCEL_CMD", },
{ SDMMC_IOCTL_RESET, "RESET", },
{ SDMMC_IOCTL_SET_CLOCK, "SET_CLOCK", },
{ SDMMC_IOCTL_SET_BUSMODE, "SET_BUSMODE", },
{ SDMMC_IOCTL_SET_HSMODE, "SET_HSMODE", },
{ SDMMC_IOCTL_SET_BOOTMODE, "SET_BOOTMODE", },
{ SDMMC_IOCTL_SET_LENPREFIX, "SET_LENPREFIX", },
{ SDMMC_IOCTL_GET_CLOCK, "GET_CLOCK", },
{ SDMMC_IOCTL_GET_BUSMODE, "GET_BUSMODE", },
{ SDMMC_IOCTL_GET_HSMODE, "GET_HSMODE", },
{ SDMMC_IOCTL_GET_BOOTMODE, "GET_BOOTMODE", },
{ SDMMC_IOCTL_GET_XFERCOMPL, "GET_XFERCOMPL", },
{ SDMMC_IOCTL_GET_DEVICE, "GET_DEVICE", },
};
const char * SD_StringifyRetCode(uint32_t dwRCode)
{
const uint8_t bound = ARRAY_SIZE(sdmmcRCodeNames);
uint8_t ix;
for (ix = 0; ix < bound; ix++) {
if (dwRCode == (uint32_t)sdmmcRCodeNames[ix].key)
return sdmmcRCodeNames[ix].name;
}
return sdmmcInvalidCode;
}
const char * SD_StringifyIOCtrl(uint32_t dwCtrl)
{
const uint8_t bound = ARRAY_SIZE(sdmmcIOCtrlNames);
uint8_t ix;
for (ix = 0; ix < bound; ix++) {
if (dwCtrl == (uint32_t)sdmmcIOCtrlNames[ix].key)
return sdmmcIOCtrlNames[ix].name;
}
return sdmmcInvalidCode;
}
#endif
uint8_t SdDecideBuswidth(SdmmcDriver *drv)
{
uint8_t error, busWidth = 1;
const uint8_t sd = (drv->card.bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD;
const uint8_t io = (drv->card.bCardType & CARD_TYPE_bmSDIO) != 0;
if (io)
busWidth = 1; /* SDIO => 1 bit only. TODO: assign CCCR. */
else if (sd) {
busWidth = 4; /* default to 4-bit mode */
error = HwSetBusWidth(drv, busWidth);
if (error)
busWidth = 1;
}
/* Switch to selected bus mode */
if (sd && busWidth > 1)
error = Acmd6(drv, busWidth);
else
error = HwSetBusWidth(drv, busWidth);
if (error)
return error;
drv->card.bBusMode = busWidth;
return 0;
}
uint8_t SdEnableHighSpeed(SdmmcDriver *drv)
{
SdCmd6Arg request = {
.acc_mode = 0xf,
.cmd_sys = 0xf,
.drv_strgth = 0xf,
.pwr_limit = 0xf,
.func_grp5 = 0xf,
.func_grp6 = 0xf,
.set = 0,
};
uint32_t status;
uint16_t mode_mask, val;
uint8_t mode = drv->card.bCardSigLevel ? SDMMC_TIM_SD_DS : SDMMC_TIM_SD_SDR12;
uint8_t error, mode_func, pwr_func = SD_SWITCH_ST_MAX_PWR_1_44W;
const bool has_io = drv->card.bCardType & CARD_TYPE_bmSDIO ? true : false;
const bool has_mem = drv->card.bCardType & CARD_TYPE_bmSD ? true : false;
const bool has_switch = SD_CSD_CCC(drv->card.CSD) & 1 << 10 ? true : false;
bool sfs_v1 = false;
//assert(sizeof(pSd->sandbox1) >= 512 / 8);
#ifndef SDMMC_TRIM_SDIO
/* TODO consider the UHS-I timing modes for SDIO devices too */
if (has_io && !(has_mem && !has_switch)
&& HwIsTimingSupported(drv, SDMMC_TIM_SD_HS)) {
/* Check CIA.HS */
status = 0;
error = Cmd52(drv, 0, SDIO_CIA, 0, SDIO_HS_REG, &status);
if (error)
return SDMMC_ERR;
if (status & SDIO_SHS) {
/* Enable High Speed timing mode */
status = SDIO_EHS;
error = Cmd52(drv, 1, SDIO_CIA, 1, SDIO_HS_REG,
&status);
if (error || !(status & SDIO_EHS))
return SDMMC_ERR;
mode = SDMMC_TIM_SD_HS;
}
}
#endif
if (!has_mem || !has_switch)
goto Apply;
/* Search for the fastest supported timing mode */
error = SdCmd6(drv, &request, drv->card.sandbox1, &status);
if (error || status & STATUS_SWITCH_ERROR)
return SDMMC_ERR;
sfs_v1 = SD_SWITCH_ST_DATA_STRUCT_VER(drv->card.sandbox1) >= 0x01;
mode_mask = SD_SWITCH_ST_FUN_GRP1_INFO(drv->card.sandbox1);
TRACE_1("Device timing functions: 0x%04x\n\r", mode_mask);
if (has_io && mode == SDMMC_TIM_SD_HS
&& !(mode_mask & 1 << SD_SWITCH_ST_ACC_HS))
return SDMMC_NOT_SUPPORTED;
else if (has_io) {
/* Have SDMEM use the same timing mode as SDIO */
} else if (mode_mask & 1 << SD_SWITCH_ST_ACC_SDR104
&& HwIsTimingSupported(drv, SDMMC_TIM_SD_SDR104))
mode = SDMMC_TIM_SD_SDR104;
else if (mode_mask & 1 << SD_SWITCH_ST_ACC_DDR50
&& HwIsTimingSupported(drv, SDMMC_TIM_SD_DDR50))
mode = SDMMC_TIM_SD_DDR50;
else if (mode_mask & 1 << SD_SWITCH_ST_ACC_SDR50
&& HwIsTimingSupported(drv, SDMMC_TIM_SD_SDR50))
mode = SDMMC_TIM_SD_SDR50;
else if (mode_mask & 1 << SD_SWITCH_ST_ACC_HS
&& HwIsTimingSupported(drv, SDMMC_TIM_SD_HS))
mode = SDMMC_TIM_SD_HS;
else
mode = SDMMC_TIM_SD_DS;
/* Verify current signaling level is the one expected by the device */
if ((mode >= SDMMC_TIM_SD_SDR50 && drv->card.bCardSigLevel != 0)
|| (mode < SDMMC_TIM_SD_SDR50 && drv->card.bCardSigLevel == 0))
return SDMMC_STATE;
/* Check the electrical power requirements of this device */
val = SD_SWITCH_ST_FUN_GRP4_INFO(drv->card.sandbox1);
TRACE_2("Device pwr & strength functions: 0x%04x & 0x%04x\n\r", val,
SD_SWITCH_ST_FUN_GRP3_INFO(drv->card.sandbox1));
if (!(val & 1 << SD_SWITCH_ST_MAX_PWR_1_44W))
pwr_func = SD_SWITCH_ST_MAX_PWR_0_72W;
request.acc_mode = mode_func = SdGetTimingFunction(mode);
request.drv_strgth = SD_SWITCH_ST_OUT_DRV_B;
request.pwr_limit = SD_SWITCH_ST_MAX_PWR_0_72W;
error = SdCmd6(drv, &request, drv->card.sandbox1, &status);
if (error || status & STATUS_SWITCH_ERROR)
return SDMMC_ERR;
val = SD_SWITCH_ST_MAX_CURR_CONSUMPTION(drv->card.sandbox1);
TRACE_1("Device max current: %u mA\n\r", val);
if (val == 0 || val > (1440 * 10) / 36)
SdSelectSlowerTiming(drv->card.bCardSigLevel != 0, &mode);
else if (sfs_v1) {
val = SD_SWITCH_ST_FUN_GRP4_BUSY(drv->card.sandbox1);
if (val & 1 << SD_SWITCH_ST_MAX_PWR_1_44W)
pwr_func = SD_SWITCH_ST_MAX_PWR_0_72W;
val = SD_SWITCH_ST_FUN_GRP1_BUSY(drv->card.sandbox1);
if (val & 1 << mode_func)
SdSelectSlowerTiming(drv->card.bCardSigLevel != 0, &mode);
}
/* Select device output Driver Type B, i.e. 50 ohm nominal output
* impedance.
* FIXME select the optimal device output Driver Type, which depends on
* board design. An oscilloscope should be used to observe signal
* integrity, then among the driver types that meet rise and fall time
* requirements, the weakest should be selected.
*/
request.acc_mode = 0xf;
request.pwr_limit = 0xf;
request.set = 1;
error = SdCmd6(drv, &request, drv->card.sandbox1, &status);
if (error || status & STATUS_SWITCH_ERROR)
return SDMMC_ERR;
val = SD_SWITCH_ST_FUN_GRP3_RC(drv->card.sandbox1);
if (val != request.drv_strgth)
SdSelectSlowerTiming(drv->card.bCardSigLevel != 0, &mode);
Switch:
/* Now switch the memory device to the candidating mode */
request.acc_mode = mode_func = SdGetTimingFunction(mode);
request.cmd_sys = 0x0;
request.drv_strgth = 0xf;
request.pwr_limit = pwr_func;
error = SdCmd6(drv, &request, drv->card.sandbox1, &status);
if (error || status & STATUS_SWITCH_ERROR)
return SDMMC_ERR;
val = SD_SWITCH_ST_FUN_GRP1_RC(drv->card.sandbox1);
while (val != mode_func && val != SD_SWITCH_ST_FUN_GRP_RC_ERROR) {
/* FIXME break upon timeout condition */
request.acc_mode = 0xf;
request.cmd_sys = 0xf;
request.pwr_limit = 0xf;
request.set = 0;
error = SdCmd6(drv, &request, drv->card.sandbox1, &status);
if (error || status & STATUS_SWITCH_ERROR)
return SDMMC_ERR;
val = SD_SWITCH_ST_FUN_GRP1_RC(drv->card.sandbox1);
if (val != mode_func && sfs_v1
&& !(SD_SWITCH_ST_FUN_GRP1_BUSY(drv->card.sandbox1)
& 1 << mode_func))
break;
}
if (val != mode_func && (mode == SDMMC_TIM_SD_DS || mode == SDMMC_TIM_SD_SDR12))
return SDMMC_ERR;
else if (val != mode_func) {
SdSelectSlowerTiming(drv->card.bCardSigLevel != 0, &mode);
goto Switch;
}
val = SD_SWITCH_ST_FUN_GRP4_RC(drv->card.sandbox1);
if (val != pwr_func) {
TRACE_1("Device power limit 0x%x\n\r", val);
}
Apply:
error = HwSetHsMode(drv, mode);
if (error == SDMMC_OK)
drv->card.bSpeedMode = mode;
else
return SDMMC_ERR;
return SDMMC_OK;
}
void SdGetExtInformation(SdmmcDriver *drv)
{
uint32_t card_status;
uint8_t error;
error = Acmd51(drv, drv->card.SCR, &card_status);
if (error == SDMMC_OK) {
card_status &= ~STATUS_READY_FOR_DATA;
if (card_status != (STATUS_APP_CMD | STATUS_TRAN)) {
TRACE_1("SCR st %lx\n\r", card_status);
}
}
error = Acmd13(drv, drv->card.SSR, &card_status);
if (error == SDMMC_OK) {
card_status &= ~STATUS_READY_FOR_DATA;
if (card_status != (STATUS_APP_CMD | STATUS_TRAN)) {
TRACE_1("SSR st %lx\n\r", card_status);
}
}
}
/**
* Reset SD/MMC driver runtime parameters.
*/
void SdParamReset(sSdCard * pSd)
{
pSd->dwTotalSize = 0;
pSd->dwNbBlocks = 0;
pSd->wBlockSize = 0;
pSd->wCurrBlockLen = 0;
pSd->dwCurrSpeed = 0;
pSd->wAddress = 0;
pSd->bCardType = 0;
pSd->bCardSigLevel = 2;
pSd->bSpeedMode = SDMMC_TIM_MMC_BC;
pSd->bBusMode = 1;
pSd->bStatus = SDMMC_NOT_INITIALIZED;
pSd->bSetBlkCnt = 0;
pSd->bStopMultXfer = 0;
/* Clear our device register cache */
memset(pSd->CID, 0, 16);
memset(pSd->CSD, 0, 16);
memset(pSd->EXT, 0, EXT_SIZE);
memset(pSd->SSR, 0, SSR_SIZE);
memset(pSd->SCR, 0, SCR_SIZE);
}
/**
* From a wide-width device register extract the requested field.
* \param reg Contents of the register
* \param reg_len Length of the register, in bits
* \param field_start Offset (address of the least significant bit) of the
* requested field, in bits
* \param field_len Length of the requested field, in bits
* \return The value of the field.
*/
uint32_t SD_GetField(const uint8_t *reg, uint16_t reg_len, uint16_t field_start,
uint8_t field_len)
{
uint32_t val = 0;
uint8_t byte, expected_bits = field_len, new_bits;
//assert(reg);
//assert(reg_len % 8 == 0);
//assert(field_len != 0 && field_len <= 32 && field_len <= reg_len);
//assert(field_start <= reg_len - field_len);
reg += (reg_len - field_start - field_len) / 8;
while (expected_bits) {
byte = *reg;
new_bits = (field_start + expected_bits) % 8;
if (new_bits)
byte &= (1 << new_bits) - 1;
else
new_bits = 8;
if (new_bits <= expected_bits)
val |= (uint32_t)byte << (expected_bits - new_bits);
else {
byte >>= new_bits - expected_bits;
val |= byte;
new_bits = expected_bits;
}
expected_bits -= new_bits;
reg++;
}
//assert((val & ~0 << field_len) == 0);
return val;
}
static uint8_t SdGetTimingFunction(uint8_t mode) {
if (mode == SDMMC_TIM_SD_SDR104)
return SD_SWITCH_ST_ACC_SDR104;
else if (mode == SDMMC_TIM_SD_DDR50)
return SD_SWITCH_ST_ACC_DDR50;
else if (mode == SDMMC_TIM_SD_SDR50)
return SD_SWITCH_ST_ACC_SDR50;
else if (mode == SDMMC_TIM_SD_HS || mode == SDMMC_TIM_SD_SDR25)
return SD_SWITCH_ST_ACC_HS;
else
return SD_SWITCH_ST_ACC_DS;
}
static void SdSelectSlowerTiming(bool high_sig, uint8_t * mode)
{
if (high_sig)
*mode = SDMMC_TIM_SD_DS;
else if (*mode > SDMMC_TIM_SD_SDR50)
*mode = SDMMC_TIM_SD_SDR50;
else if (*mode > SDMMC_TIM_SD_SDR25)
*mode = SDMMC_TIM_SD_SDR25;
else
*mode = SDMMC_TIM_SD_SDR12;
}
uint32_t SD_GetTotalSizeKB(const sSdCard * pSd)
{
//assert(pSd != NULL);
if (pSd->dwTotalSize == 0xFFFFFFFF)
return (pSd->dwNbBlocks / 1024) * pSd->wBlockSize;
else
return pSd->dwTotalSize / 1024;
}
void SD_DumpStatus(const sSdCard *pSd)
{
char text[40] = "";
char mode[20] = "";
char vers[7] = { ' ', 'v', '1', '.', '0', '\0', '\0' };
//assert(pSd != NULL);
if (pSd->bCardType & CARD_TYPE_bmHC)
strcat(text, "High-capacity ");
if (pSd->bCardType & CARD_TYPE_bmSDIO
&& pSd->bCardType & CARD_TYPE_bmSD)
strcat(text, "SDIO combo card");
else if (pSd->bCardType & CARD_TYPE_bmSDIO)
strcat(text, "SDIO device");
else if (pSd->bCardType & CARD_TYPE_bmSD)
strcat(text, "SD card");
#ifndef SDMMC_TRIM_MMC
else if (pSd->bCardType & CARD_TYPE_bmMMC)
strcat(text, "MMC device");
#endif
else
strcat(text, "unrecognized device");
if (pSd->bCardType & CARD_TYPE_bmMMC) {
#ifndef SDMMC_TRIM_MMC
const uint8_t csd = MMC_CSD_SPEC_VERS(pSd->CSD);
const uint8_t ext = MMC_EXT_EXT_CSD_REV(pSd->EXT);
if (csd == MMC_CSD_SPEC_VERS_1_4)
vers[4] = '4';
else if (csd == MMC_CSD_SPEC_VERS_2_0) {
vers[2] = '2';
vers[4] = 'x';
}
else if (csd == MMC_CSD_SPEC_VERS_3_1) {
vers[2] = '3';
vers[4] = 'x';
}
else if (csd == MMC_CSD_SPEC_VERS_4_0) {
vers[2] = ext <= 6 ? '4' : '5';
if (ext <= 4)
vers[4] = '0' + ext;
else if (ext == 5) {
vers[4] = '4';
vers[5] = '1';
}
else if (ext == 6) {
vers[4] = '5';
vers[5] = 'x';
}
else if (ext == 7)
vers[5] = 'x';
else if (ext == 8)
vers[4] = '1';
else
vers[4] = 'x';
}
else if (csd != MMC_CSD_SPEC_VERS_1_0)
vers[2] = vers[4] = '?';
strcat(text, vers);
#endif
}
else if (pSd->bCardType & CARD_TYPE_bmSD
&& SD_SCR_STRUCTURE(pSd->SCR) == SD_SCR_STRUCTURE_1_0) {
if (SD_SCR_SD_SPEC(pSd->SCR) == SD_SCR_SD_SPEC_1_0)
vers[5] = 'x';
else if (SD_SCR_SD_SPEC(pSd->SCR) == SD_SCR_SD_SPEC_1_10) {
vers[4] = '1';
vers[5] = '0';
}
else if (SD_SCR_SD_SPEC(pSd->SCR) == SD_SCR_SD_SPEC_2_00) {
if (SD_SCR_SD_SPEC4(pSd->SCR) == SD_SCR_SD_SPEC_4_X) {
vers[2] = '4';
vers[4] = vers[5] = 'x';
}
else if (SD_SCR_SD_SPEC3(pSd->SCR)
== SD_SCR_SD_SPEC_3_0) {
vers[2] = '3';
vers[5] = 'x';
}
else {
vers[2] = '2';
vers[5] = '0';
}
}
else
vers[2] = vers[4] = '?';
strcat(text, vers);
}
if (pSd->bSpeedMode == SDMMC_TIM_MMC_BC)
strcat(mode, "Backward-compatible");
#ifndef SDMMC_TRIM_MMC
else if (pSd->bSpeedMode == SDMMC_TIM_MMC_HS_SDR)
strcat(mode, "HS SDR");
else if (pSd->bSpeedMode == SDMMC_TIM_MMC_HS_DDR)
strcat(mode, "HS DDR");
else if (pSd->bSpeedMode == SDMMC_TIM_MMC_HS200)
strcat(mode, "HS200");
#endif
else if (pSd->bSpeedMode == SDMMC_TIM_SD_DS)
strcat(mode, "DS");
else if (pSd->bSpeedMode == SDMMC_TIM_SD_HS)
strcat(mode, "HS");
else if (pSd->bSpeedMode >= SDMMC_TIM_SD_SDR12
&& pSd->bSpeedMode <= SDMMC_TIM_SD_SDR104) {
char uhs_mode[10] = "UHS-I SDR";
if (pSd->bSpeedMode == SDMMC_TIM_SD_DDR50)
uhs_mode[6] = 'D';
strcat(mode, uhs_mode);
if (pSd->bSpeedMode == SDMMC_TIM_SD_SDR12)
strcat(mode, "12");
else if (pSd->bSpeedMode == SDMMC_TIM_SD_SDR25)
strcat(mode, "25");
else if (pSd->bSpeedMode == SDMMC_TIM_SD_SDR50
|| pSd->bSpeedMode == SDMMC_TIM_SD_DDR50)
strcat(mode, "50");
else
strcat(mode, "104");
}
TRACE_4("%s, %u-bit data, in %s mode at %lu kHz\n\r", text, pSd->bBusMode, mode, (pSd->dwCurrSpeed / 1000UL) );
if (pSd->bCardType & CARD_TYPE_bmSDMMC) {
TRACE_3("Device memory size: %lu MiB, %lu * %uB\n\r", SD_GetTotalSizeKB(pSd) / 1024ul, pSd->dwNbBlocks,pSd->wBlockSize);
}
}
/**
* Display the content of the CID register
* \param pSd Pointer to SdCard instance.
*/
void SD_DumpCID(const sSdCard *pSd)
{
const uint8_t sd_device = (pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD;
/* Function-only SDIO devices have no CID register */
if ((pSd->bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmUNKNOWN)
return;
TRACE("Card IDentification\r\n");
TRACE_1("MID 0x%02X\r\n", SD_CID_MID(pSd->CID));
if (sd_device) {
TRACE_2("OID %c%c\r\n", (char) SD_CID_OID1(pSd->CID),(char) SD_CID_OID0(pSd->CID));
TRACE_5("PNM %c%c%c%c%c\r\n", (char) SD_CID_PNM4(pSd->CID),
(char) SD_CID_PNM3(pSd->CID), (char) SD_CID_PNM2(pSd->CID),
(char) SD_CID_PNM1(pSd->CID), (char) SD_CID_PNM0(pSd->CID));
TRACE_2("PRV %u.%u\r\n", SD_CID_PRV1(pSd->CID),
SD_CID_PRV0(pSd->CID));
TRACE_4("PSN 0x%02X%02X%02X%02X\r\n", SD_CID_PSN3(pSd->CID),
SD_CID_PSN2(pSd->CID), SD_CID_PSN1(pSd->CID),
SD_CID_PSN0(pSd->CID));
TRACE_2("MDT %u/%02u\r\n", 2000 + SD_CID_MDT_Y(pSd->CID),
SD_CID_MDT_M(pSd->CID));
}
#ifndef SDMMC_TRIM_MMC
else {
uint16_t year = 1997 + MMC_CID_MDT_Y(pSd->CID);
if (MMC_EXT_EXT_CSD_REV(pSd->EXT) >= 3) {
TRACE_1("CBX %u\r\n", eMMC_CID_CBX(pSd->CID));
TRACE_1("OID 0x%02X\r\n", eMMC_CID_OID(pSd->CID));
}
else {
TRACE_1("OID 0x%04X\r\n", MMC_CID_OID(pSd->CID));
}
TRACE_6("PNM %c%c%c%c%c%c\r\n",
(char) MMC_CID_PNM5(pSd->CID),
(char) MMC_CID_PNM4(pSd->CID),
(char) MMC_CID_PNM3(pSd->CID),
(char) MMC_CID_PNM2(pSd->CID),
(char) MMC_CID_PNM1(pSd->CID),
(char) MMC_CID_PNM0(pSd->CID));
TRACE_2("PRV %u.%u\r\n", MMC_CID_PRV1(pSd->CID),
MMC_CID_PRV0(pSd->CID));
TRACE_4("PSN 0x%02X%02X%02X%02X\r\n", MMC_CID_PSN3(pSd->CID),
MMC_CID_PSN2(pSd->CID), MMC_CID_PSN1(pSd->CID),
MMC_CID_PSN0(pSd->CID));
if (MMC_EXT_EXT_CSD_REV(pSd->EXT) > 4 && year < 2010)
year = year - 1997 + 2013;
TRACE_2("MDT %u/%02u\r\n", year, MMC_CID_MDT_M(pSd->CID));
}
#endif
TRACE_1("CRC 0x%02X\r\n", SD_CID_CRC(pSd->CID));
}
/**
* Display the content of the SCR register
* \param pSCR Pointer to SCR data.
*/
void SD_DumpSCR(const uint8_t *pSCR)
{
(void)pSCR;
_PrintTitle("SD Card Configuration");
_PrintField("SCR_STRUCT 0x%X\r\n", SD_SCR_STRUCTURE(pSCR));
_PrintField("SD_SPEC 0x%X\r\n", SD_SCR_SD_SPEC(pSCR));
_PrintField("SD_SPEC3 %u\r\n", SD_SCR_SD_SPEC3(pSCR));
_PrintField("SD_SPEC4 %u\r\n", SD_SCR_SD_SPEC4(pSCR));
_PrintField("DATA_ST_AFTER_ER %u\r\n",
SD_SCR_DATA_STAT_AFTER_ERASE(pSCR));
_PrintField("SD_SEC 0x%X\r\n", SD_SCR_SD_SECURITY(pSCR));
_PrintField("EX_SEC 0x%X\r\n", SD_SCR_EX_SECURITY(pSCR));
_PrintField("SD_BUS_WIDTHS 0x%X\r\n", SD_SCR_SD_BUS_WIDTHS(pSCR));
_PrintField("CMD20 %u\r\n", SD_SCR_CMD20_SUPPORT(pSCR));
_PrintField("CMD23 %u\r\n", SD_SCR_CMD23_SUPPORT(pSCR));
_PrintField("CMD48/49 %u\r\n", SD_SCR_CMD48_SUPPORT(pSCR));
_PrintField("CMD58/59 %u\r\n", SD_SCR_CMD58_SUPPORT(pSCR));
}
/**
* Display the content of the SD Status Register
* \param pSSR Pointer to SSR data.
*/
void SD_DumpSSR(const uint8_t *pSSR)
{
(void)pSSR;
_PrintTitle("SD Status");
_PrintField("DAT_BUS_WIDTH 0x%X\r\n", SD_SSR_DAT_BUS_WIDTH(pSSR));
_PrintField("SEC_MODE %u\r\n", SD_SSR_SECURED_MODE(pSSR));
_PrintField("SD_CARD_TYPE 0x%04X\r\n", SD_SSR_CARD_TYPE(pSSR));
_PrintField("PAREA_SIZE %lu\r\n",
SD_SSR_SIZE_OF_PROTECTED_AREA(pSSR));
_PrintField("SPD_CLASS 0x%02X\r\n", SD_SSR_SPEED_CLASS(pSSR));
_PrintField("UHS_SPD_GRADE 0x%X\r\n", SD_SSR_UHS_SPEED_GRADE(pSSR));
_PrintField("PE_MOVE %u MB/sec\r\n", SD_SSR_PERFORMANCE_MOVE(pSSR));
_PrintField("AU_SIZE 0x%X\r\n", SD_SSR_AU_SIZE(pSSR));
_PrintField("UHS_AU_SIZE 0x%X\r\n", SD_SSR_UHS_AU_SIZE(pSSR));
_PrintField("ER_SIZE %u AU\r\n", SD_SSR_ERASE_SIZE(pSSR));
_PrintField("ER_TIMEOUT %u sec\r\n", SD_SSR_ERASE_TIMEOUT(pSSR));
_PrintField("ER_OFFS %u sec\r\n", SD_SSR_ERASE_OFFSET(pSSR));
}
/**
* Display the content of the CSD register
* \param pSd Pointer to SdCard instance.
*/
void SD_DumpCSD(const sSdCard *pSd)
{
const uint8_t sd_device = (pSd->bCardType & CARD_TYPE_bmSDMMC)
== CARD_TYPE_bmSD;
const uint8_t sd_csd_v2 = sd_device
&& SD_CSD_STRUCTURE(pSd->CSD) >= 0x1;
_PrintTitle("Card-Specific Data");
_PrintField("CSD_STRUCT 0x%X\r\n", SD_CSD_STRUCTURE(pSd->CSD));
#ifndef SDMMC_TRIM_MMC
if (!sd_device) {
_PrintField("SPEC_V 0x%X\r\n", MMC_CSD_SPEC_VERS(pSd->CSD));
}
#endif
_PrintField("TAAC 0x%X\r\n", SD_CSD_TAAC(pSd->CSD));
_PrintField("NSAC 0x%X\r\n", SD_CSD_NSAC(pSd->CSD));
_PrintField("TRAN_SPD 0x%X\r\n", SD_CSD_TRAN_SPEED(pSd->CSD));
_PrintField("CCC 0x%X\r\n", SD_CSD_CCC(pSd->CSD));
_PrintField("RD_BL_LEN 0x%X\r\n", SD_CSD_READ_BL_LEN(pSd->CSD));
_PrintField("RD_BL_PART %u\r\n", SD_CSD_READ_BL_PARTIAL(pSd->CSD));
_PrintField("WR_BL_MALIGN %u\r\n", SD_CSD_WRITE_BLK_MISALIGN(pSd->CSD));
_PrintField("RD_BL_MALIGN %u\r\n", SD_CSD_READ_BLK_MISALIGN(pSd->CSD));
_PrintField("DSR_IMP %u\r\n", SD_CSD_DSR_IMP(pSd->CSD));
_PrintField("C_SIZE 0x%lX\r\n", sd_csd_v2 ? SD2_CSD_C_SIZE(pSd->CSD)
: SD_CSD_C_SIZE(pSd->CSD));
if (!sd_csd_v2) {
_PrintField("RD_CUR_MIN 0x%X\r\n",
SD_CSD_VDD_R_CURR_MIN(pSd->CSD));
_PrintField("RD_CUR_MAX 0x%X\r\n",
SD_CSD_VDD_R_CURR_MAX(pSd->CSD));
_PrintField("WR_CUR_MIN 0x%X\r\n",
SD_CSD_VDD_W_CURR_MIN(pSd->CSD));
_PrintField("WR_CUR_MAX 0x%X\r\n",
SD_CSD_VDD_W_CURR_MAX(pSd->CSD));
_PrintField("C_SIZE_MULT 0x%X\r\n",
SD_CSD_C_SIZE_MULT(pSd->CSD));
}
if (sd_device) {
_PrintField("ER_BL_EN %u\r\n", SD_CSD_ERASE_BLK_EN(pSd->CSD));
_PrintField("SECT_SIZE 0x%X\r\n", SD_CSD_SECTOR_SIZE(pSd->CSD));
}
#ifndef SDMMC_TRIM_MMC
else {
_PrintField("ER_GRP_SIZE 0x%X\r\n",
MMC_CSD_ERASE_GRP_SIZE(pSd->CSD));
_PrintField("ER_GRP_MULT 0x%X\r\n",
MMC_CSD_ERASE_GRP_MULT(pSd->CSD));
}
#endif
#ifdef SDMMC_TRIM_MMC
_PrintField("WP_GRP_SIZE 0x%X\r\n", SD_CSD_WP_GRP_SIZE(pSd->CSD));
#else
_PrintField("WP_GRP_SIZE 0x%X\r\n", sd_device ?
SD_CSD_WP_GRP_SIZE(pSd->CSD) : MMC_CSD_WP_GRP_SIZE(pSd->CSD));
#endif
_PrintField("WP_GRP_EN %u\r\n", SD_CSD_WP_GRP_ENABLE(pSd->CSD));
#ifndef SDMMC_TRIM_MMC
if (!sd_device) {
_PrintField("DEF_ECC 0x%X\r\n", MMC_CSD_DEFAULT_ECC(pSd->CSD));
}
#endif
_PrintField("R2W_FACT 0x%X\r\n", SD_CSD_R2W_FACTOR(pSd->CSD));
_PrintField("WR_BL_LEN 0x%X\r\n", SD_CSD_WRITE_BL_LEN(pSd->CSD));
_PrintField("WR_BL_PART %u\r\n", SD_CSD_WRITE_BL_PARTIAL(pSd->CSD));
_PrintField("FILE_FMT_GRP %u\r\n", SD_CSD_FILE_FORMAT_GRP(pSd->CSD));
_PrintField("COPY %u\r\n", SD_CSD_COPY(pSd->CSD));
_PrintField("PERM_WP %u\r\n", SD_CSD_PERM_WRITE_PROTECT(pSd->CSD));
_PrintField("TMP_WP %u\r\n", SD_CSD_TMP_WRITE_PROTECT(pSd->CSD));
_PrintField("FILE_FMT 0x%X\r\n", SD_CSD_FILE_FORMAT(pSd->CSD));
#ifndef SDMMC_TRIM_MMC
if (!sd_device) {
_PrintField("ECC 0x%X\r\n", MMC_CSD_ECC(pSd->CSD));
}
#endif
_PrintField("CRC 0x%X\r\n", SD_CSD_CRC(pSd->CSD));
}
/**
* Display the content of the EXT_CSD register
* \param pExtCSD Pointer to extended CSD data.
*/
void SD_DumpExtCSD(const uint8_t *pExtCSD)
{
(void)pExtCSD;
_PrintTitle("Extended Device Specific Data");
_PrintField("S_CMD_SET 0x%X\r\n", MMC_EXT_S_CMD_SET(pExtCSD));
_PrintField("BOOT_INFO 0x%X\r\n", MMC_EXT_BOOT_INFO(pExtCSD));
_PrintField("BOOT_SIZE_MULTI 0x%X\r\n",
MMC_EXT_BOOT_SIZE_MULTI(pExtCSD));
_PrintField("ACC_SIZE 0x%X\r\n", MMC_EXT_ACC_SIZE(pExtCSD));
_PrintField("HC_ER_GRP_SIZE 0x%X\r\n",
MMC_EXT_HC_ERASE_GRP_SIZE(pExtCSD));
_PrintField("ER_TIMEOUT_MULT 0x%X\r\n",
MMC_EXT_ERASE_TIMEOUT_MULT(pExtCSD));
_PrintField("REL_WR_SEC_C 0x%X\r\n", MMC_EXT_REL_WR_SEC_C(pExtCSD));
_PrintField("HC_WP_GRP_SIZE 0x%X\r\n", MMC_EXT_HC_WP_GRP_SIZE(pExtCSD));
_PrintField("S_C_VCC 0x%X\r\n", MMC_EXT_S_C_VCC(pExtCSD));
_PrintField("S_C_VCCQ 0x%X\r\n", MMC_EXT_S_C_VCCQ(pExtCSD));
_PrintField("S_A_TIMEOUT 0x%X\r\n", MMC_EXT_S_A_TIMEOUT(pExtCSD));
_PrintField("SEC_CNT 0x%lX\r\n", MMC_EXT_SEC_COUNT(pExtCSD));
_PrintField("MIN_PE_W_8_52 0x%X\r\n", MMC_EXT_MIN_PERF_W_8_52(pExtCSD));
_PrintField("MIN_PE_R_8_52 0x%X\r\n", MMC_EXT_MIN_PERF_R_8_52(pExtCSD));
_PrintField("MIN_PE_W_8_26_4_52 0x%X\r\n",
MMC_EXT_MIN_PERF_W_8_26_4_52(pExtCSD));
_PrintField("MIN_PE_R_8_26_4_52 0x%X\r\n",
MMC_EXT_MIN_PERF_R_8_26_4_52(pExtCSD));
_PrintField("MIN_PE_W_4_26 0x%X\r\n", MMC_EXT_MIN_PERF_W_4_26(pExtCSD));
_PrintField("MIN_PE_R_4_26 0x%X\r\n", MMC_EXT_MIN_PERF_R_4_26(pExtCSD));
_PrintField("PWR_CL_26_360 0x%X\r\n", MMC_EXT_PWR_CL_26_360(pExtCSD));
_PrintField("PWR_CL_52_360 0x%X\r\n", MMC_EXT_PWR_CL_52_360(pExtCSD));
_PrintField("PWR_CL_26_195 0x%X\r\n", MMC_EXT_PWR_CL_26_195(pExtCSD));
_PrintField("PWR_CL_52_195 0x%X\r\n", MMC_EXT_PWR_CL_52_195(pExtCSD));
_PrintField("DRV_STR 0x%X\r\n", MMC_EXT_DRV_STRENGTH(pExtCSD));
_PrintField("CARD_TYPE 0x%X\r\n", MMC_EXT_CARD_TYPE(pExtCSD));
_PrintField("CSD_STRUCT 0x%X\r\n", MMC_EXT_CSD_STRUCTURE(pExtCSD));
_PrintField("EXT_CSD_REV 0x%X\r\n", MMC_EXT_EXT_CSD_REV(pExtCSD));
_PrintField("CMD_SET 0x%X\r\n", MMC_EXT_CMD_SET(pExtCSD));
_PrintField("CMD_SET_REV 0x%X\r\n", MMC_EXT_CMD_SET_REV(pExtCSD));
_PrintField("PWR_CLASS 0x%X\r\n", MMC_EXT_POWER_CLASS(pExtCSD));
_PrintField("HS_TIM 0x%X\r\n", MMC_EXT_HS_TIMING(pExtCSD));
_PrintField("BUS_WIDTH 0x%X\r\n", MMC_EXT_BUS_WIDTH(pExtCSD));
_PrintField("ER_MEM_CONT 0x%X\r\n", MMC_EXT_ERASED_MEM_CONT(pExtCSD));
_PrintField("BOOT_CFG 0x%X\r\n", MMC_EXT_BOOT_CONFIG(pExtCSD));
_PrintField("BOOT_BUS_WIDTH 0x%X\r\n", MMC_EXT_BOOT_BUS_WIDTH(pExtCSD));
_PrintField("ER_GRP_DEF 0x%X\r\n", MMC_EXT_ERASE_GROUP_DEF(pExtCSD));
}

View File

@ -0,0 +1,111 @@
#ifndef CH_SDMMC_SD_H_
#define CH_SDMMC_SD_H_
/** \addtogroup sd_scr_acc SD SCR register fields
* @{
*/
/** SCR (Configuration register) access macros (64 bits, 2 * 32 bits, 8 * 8 bits). */
#define SD_SCR(pScr, field, bits) SD_GetField(pScr, 64, field, bits)
#define SD_SCR_STRUCTURE(pScr) (uint8_t)SD_SCR(pScr, 60, 4)
#define SD_SCR_STRUCTURE_1_0 0 /**< SD v1.01~3.01 */
#define SD_SCR_SD_SPEC(pScr) (uint8_t)SD_SCR(pScr, 56, 4)
#define SD_SCR_SD_SPEC_1_0 0 /**< SD v1.0~1.01 */
#define SD_SCR_SD_SPEC_1_10 1 /**< SD v1.10 */
#define SD_SCR_SD_SPEC_2_00 2 /**< SD v2.00 */
#define SD_SCR_DATA_STAT_AFTER_ERASE(pScr) (uint8_t)SD_SCR(pScr, 55, 1)
#define SD_SCR_SD_SECURITY(pScr) (uint8_t)SD_SCR(pScr, 52, 3)
#define SD_SCR_SD_SECURITY_NO 0 /**< No security */
#define SD_SCR_SD_SECURITY_NOTUSED 1 /**< Not used */
#define SD_SCR_SD_SECURITY_1_01 2 /**< Version 1.01 */
#define SD_SCR_SD_SECURITY_2_00 3 /**< Version 2.00 */
#define SD_SCR_SD_BUS_WIDTHS(pScr) (uint8_t)SD_SCR(pScr, 48, 4)
#define SD_SCR_SD_BUS_WIDTH_1BITS (1 << 0) /**< 1 bit (DAT0) */
#define SD_SCR_SD_BUS_WIDTH_4BITS (1 << 2) /**< 4 bit (DAT0~3) */
#define SD_SCR_SD_SPEC3(pScr) (uint8_t)SD_SCR(pScr, 47, 1)
#define SD_SCR_SD_SPEC_3_0 1 /**< SD v3.0X */
#define SD_SCR_EX_SECURITY(pScr) (uint8_t)SD_SCR(pScr, 43, 4)
#define SD_SCR_EX_SECURITY_NO 0 /**< No extended security */
#define SD_SCR_SD_SPEC4(pScr) (uint8_t)SD_SCR(pScr, 42, 1)
#define SD_SCR_SD_SPEC_4_X 1 /**< SD v4.XX */
#define SD_SCR_CMD58_SUPPORT(pScr) (uint8_t)SD_SCR(pScr, 35, 1)
#define SD_SCR_CMD48_SUPPORT(pScr) (uint8_t)SD_SCR(pScr, 34, 1)
#define SD_SCR_CMD23_SUPPORT(pScr) (uint8_t)SD_SCR(pScr, 33, 1)
#define SD_SCR_CMD20_SUPPORT(pScr) (uint8_t)SD_SCR(pScr, 32, 1)
/** \addtogroup sd_switch_status SD Switch Status fields
* @{
*/
/** SD Switch Status access macros (512 bits, 16 * 32 bits, 64 * 8 bits). */
#define SD_SWITCH_ST(p, field, bits) SD_GetField(p, 512, field, bits)
#define SD_SWITCH_ST_MAX_CURR_CONSUMPTION(p) (uint16_t)SD_SWITCH_ST(p, 496, 16)
#define SD_SWITCH_ST_FUN_GRP6_INFO(p) (uint16_t)SD_SWITCH_ST(p, 480, 16)
#define SD_SWITCH_ST_FUN_GRP5_INFO(p) (uint16_t)SD_SWITCH_ST(p, 464, 16)
#define SD_SWITCH_ST_FUN_GRP4_INFO(p) (uint16_t)SD_SWITCH_ST(p, 448, 16)
#define SD_SWITCH_ST_MAX_PWR_0_72W 0x0
#define SD_SWITCH_ST_MAX_PWR_1_44W 0x1
#define SD_SWITCH_ST_MAX_PWR_2_16W 0x2
#define SD_SWITCH_ST_MAX_PWR_2_88W 0x3
#define SD_SWITCH_ST_MAX_PWR_1_80W 0x4
#define SD_SWITCH_ST_FUN_GRP3_INFO(p) (uint16_t)SD_SWITCH_ST(p, 432, 16)
#define SD_SWITCH_ST_OUT_DRV_B 0x0
#define SD_SWITCH_ST_OUT_DRV_A 0x1
#define SD_SWITCH_ST_OUT_DRV_C 0x2
#define SD_SWITCH_ST_OUT_DRV_D 0x3
#define SD_SWITCH_ST_FUN_GRP2_INFO(p) (uint16_t)SD_SWITCH_ST(p, 416, 16)
#define SD_SWITCH_ST_FUN_GRP1_INFO(p) (uint16_t)SD_SWITCH_ST(p, 400, 16)
#define SD_SWITCH_ST_ACC_DS 0x0
#define SD_SWITCH_ST_ACC_HS 0x1
#define SD_SWITCH_ST_ACC_SDR50 0x2
#define SD_SWITCH_ST_ACC_SDR104 0x3
#define SD_SWITCH_ST_ACC_DDR50 0x4
#define SD_SWITCH_ST_FUN_GRP6_RC(p) (uint8_t) SD_SWITCH_ST(p, 396, 4)
#define SD_SWITCH_ST_FUN_GRP5_RC(p) (uint8_t) SD_SWITCH_ST(p, 392, 4)
#define SD_SWITCH_ST_FUN_GRP4_RC(p) (uint8_t) SD_SWITCH_ST(p, 388, 4)
#define SD_SWITCH_ST_FUN_GRP3_RC(p) (uint8_t) SD_SWITCH_ST(p, 384, 4)
#define SD_SWITCH_ST_FUN_GRP2_RC(p) (uint8_t) SD_SWITCH_ST(p, 380, 4)
#define SD_SWITCH_ST_FUN_GRP1_RC(p) (uint8_t) SD_SWITCH_ST(p, 376, 4)
#define SD_SWITCH_ST_FUN_GRP_RC_ERROR 0xF
#define SD_SWITCH_ST_DATA_STRUCT_VER(p) (uint8_t) SD_SWITCH_ST(p, 368, 8)
#define SD_SWITCH_ST_FUN_GRP6_BUSY(p) (uint16_t)SD_SWITCH_ST(p, 352, 16)
#define SD_SWITCH_ST_FUN_GRP5_BUSY(p) (uint16_t)SD_SWITCH_ST(p, 336, 16)
#define SD_SWITCH_ST_FUN_GRP4_BUSY(p) (uint16_t)SD_SWITCH_ST(p, 320, 16)
#define SD_SWITCH_ST_FUN_GRP3_BUSY(p) (uint16_t)SD_SWITCH_ST(p, 304, 16)
#define SD_SWITCH_ST_FUN_GRP2_BUSY(p) (uint16_t)SD_SWITCH_ST(p, 288, 16)
#define SD_SWITCH_ST_FUN_GRP1_BUSY(p) (uint16_t)SD_SWITCH_ST(p, 272, 16)
#define SD_SWITCH_ST_FUN_GRP_FUN_BUSY(funNdx) (1 << (funNdx))
/** @}*/
/** We support 2.7 ~ 3.3V cards */
#define SD_HOST_VOLTAGE_RANGE (SD_OCR_VDD_27_28 +\
SD_OCR_VDD_28_29 +\
SD_OCR_VDD_29_30 +\
SD_OCR_VDD_30_31 +\
SD_OCR_VDD_31_32 +\
SD_OCR_VDD_32_33 +\
SD_OCR_VDD_33_34 +\
SD_OCR_VDD_34_35 +\
SD_OCR_VDD_35_36 )
extern uint8_t SdDecideBuswidth(SdmmcDriver *drv);
extern uint8_t SdEnableHighSpeed(SdmmcDriver *drv);
extern uint32_t SD_GetField(const uint8_t *reg, uint16_t reg_len, uint16_t field_start,
uint8_t field_len);
extern void SdGetExtInformation(SdmmcDriver *drv);
extern void SD_DumpStatus(const sSdCard *pSd);
extern void SD_DumpCID(const sSdCard *pSd);
extern void SD_DumpSCR(const uint8_t *pSCR);
extern void SD_DumpCSD(const sSdCard *pSd);
extern void SD_DumpExtCSD(const uint8_t *pExtCSD);
extern void SD_DumpSSR(const uint8_t *pSSR);
extern const char * SD_StringifyRetCode(uint32_t dwRCode);
extern const char * SD_StringifyIOCtrl(uint32_t dwCtrl);
#endif /* CH_SDMMC_SD_H_ */

View File

@ -0,0 +1,288 @@
#include "hal.h"
#include "sama_sdmmc_lld.h"
#include "ch_sdmmc_device.h"
#include "ch_sdmmc_sdio.h"
#include "ch_sdmmc_cmds.h"
#include "ch_sdmmc_sd.h"
#ifndef SDMMC_TRIM_SDIO
uint8_t SdioInit(SdmmcDriver *driver)
{
uint32_t freq;
uint8_t error;
driver->card.bSpeedMode = SDMMC_TIM_SD_DS;
HwSetHsMode(driver, driver->card.bSpeedMode);
/* Thereafter, the host issues CMD3 (SEND_RELATIVE_ADDR) asks the
* card to publish a new relative card address (RCA), which is shorter than
* CID and which is used to address the card in the future data transfer
* mode. Once the RCA is received the card state changes to the Stand-by
* State. At this point, if the host wants to assign another RCA number, it
* can ask the card to publish a new number by sending another CMD3 command
* to the card. The last published RCA is the actual RCA number of the
* card. */
error = Cmd3(driver);
if (error)
return error;
TRACE_1("RCA=%u\n\r", driver->card.wAddress);
/* Now select the card, to TRAN state */
error = SdMmcSelect(driver,driver->card.wAddress, 0);
if (error)
return error;
/* Enable more bus width Mode */
error = SdDecideBuswidth(driver);
if (error) {
TRACE_1("Bus width %s\n\r", SD_StringifyRetCode(error));
return SDMMC_ERR;
}
/* Consider High-Speed timing mode */
error = SdEnableHighSpeed(driver);
if (error)
return error;
/* Increase device clock frequency */
freq = SdioGetMaxFreq(driver);
error = HwSetClock(driver, &freq);
driver->card.dwCurrSpeed = freq;
if (error != SDMMC_OK && error != SDMMC_CHANGED) {
TRACE_1("clk %s\n\r", SD_StringifyRetCode(error));
return error;
}
return SDMMC_OK;
}
/**
* Read one or more bytes from SDIO card, using RW_DIRECT command.
* \param pSd Pointer to SdCard instance.
* \param functionNum Function number.
* \param address First register address to read from.
* \param pData Pointer to data buffer.
* \param size Buffer size, number of bytes to read.
* \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
*/
uint8_t SDIO_ReadDirect(SdmmcDriver *sdmmcp,
uint8_t functionNum,
uint32_t address, uint8_t * pData, uint32_t size)
{
uint8_t error;
uint32_t status;
sSdCard *pSd = &sdmmcp->card;
if (pSd->bCardType & CARD_TYPE_bmSDIO) {
if (size == 0)
return SDMMC_PARAM;
while (size--) {
status = 0;
error =
Cmd52(sdmmcp, 0, functionNum, 0, address++, &status);
if (pData)
*pData++ = (uint8_t) status;
if (error || status & STATUS_SDIO_R5) {
//trace_error("IOrdRegs %luB@%lu %s st %lx\n\r",
// size, address, SD_StringifyRetCode(error),
// status);
return SDMMC_ERR;
}
}
} else {
return SDMMC_NOT_SUPPORTED;
}
return 0;
}
/**
* Find SDIO ManfID, Fun0 tuple.
* \param pSd Pointer to \ref sSdCard instance.
* \param address Search area start address.
* \param size Search area size.
* \param pAddrManfID Pointer to ManfID address value buffer.
* \param pAddrFunc0 Pointer to Func0 address value buffer.
*/
uint8_t SdioFindTuples(SdmmcDriver *sdmmcp,
uint32_t address, uint32_t size,
uint32_t * pAddrManfID, uint32_t * pAddrFunc0)
{
uint8_t error, tmp[3];
uint32_t addr = address;
uint8_t flagFound = 0; /* 1:Manf, 2:Func0 */
uint32_t addManfID = 0, addFunc0 = 0;
for (; flagFound != 3;) {
error = SDIO_ReadDirect(sdmmcp, SDIO_CIA, addr, tmp, 3);
if (error)
return error;
/* End */
if (tmp[0] == CISTPL_END)
break;
/* ManfID */
else if (tmp[0] == CISTPL_MANFID) {
flagFound |= 1;
addManfID = addr;
}
/* Func0 */
else if (tmp[0] == CISTPL_FUNCE && tmp[2] == 0x00) {
flagFound |= 2;
addFunc0 = addr;
}
/* Tuple error ? */
if (tmp[1] == 0)
break;
/* Next address */
addr += (tmp[1] + 2);
if (addr > (address + size))
break;
}
if (pAddrManfID)
*pAddrManfID = addManfID;
if (pAddrFunc0)
*pAddrFunc0 = addFunc0;
return 0;
}
uint32_t SdioGetMaxFreq(SdmmcDriver *sdmmcp)
{
uint8_t error;
uint32_t addr = 0, rate;
uint8_t buf[6];
/* Check Func0 tuple in CIS area */
error = SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_CIS_PTR_REG,(uint8_t *)&addr, 3);
if (error)
return 0;
error = SdioFindTuples(sdmmcp, addr, 256, NULL, &addr);
if (error || !addr)
return 0;
/* Fun0 tuple: fn0_blk_siz & max_tran_speed */
error = SDIO_ReadDirect(sdmmcp, SDIO_CIA, addr, buf, 6);
if (error)
return 0;
rate = SdmmcDecodeTransSpeed(buf[5], sdmmcTransUnits,
sdTransMultipliers);
if (sdmmcp->card.bSpeedMode == SDMMC_TIM_SD_SDR104 && rate == 200000ul)
rate = 208000ul;
else if (sdmmcp->card.bSpeedMode == SDMMC_TIM_SD_DDR50)
rate /= 2ul;
else if (sdmmcp->card.bSpeedMode == SDMMC_TIM_SD_HS && rate == 25000ul)
rate *= 2ul;
return rate * 1000ul;
}
/**
* Display SDIO card informations (CIS, tuple ...)
* \param pSd Pointer to \ref sSdCard instance.
*/
void SDIO_DumpCardInformation(SdmmcDriver *sdmmcp)
{
uint32_t tmp = 0, addrCIS = 0, addrManfID = 0, addrFuncE = 0;
uint8_t *p = (uint8_t *) & tmp;
uint8_t buf[16];
/* CCCR */
_PrintTitle("CCCR");
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_CCCR_REG, p, 1);
_PrintField("SDIO 0x%02lX", (tmp & SDIO_SDIO) >> 4);
_PrintField("CCCR 0x%02lX", (tmp & SDIO_CCCR) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_SD_REV_REG, p, 1);
_PrintField("SD 0x%02lX", (tmp & SDIO_SD) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_IOE_REG, p, 1);
_PrintField("IOE 0x%02lX", (tmp & SDIO_IOE) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_IOR_REG, p, 1);
_PrintField("IOR 0x%02lX", (tmp & SDIO_IOR) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_IEN_REG, p, 1);
_PrintField("IEN 0x%02lX", (tmp & SDIO_IEN) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_INT_REG, p, 1);
_PrintField("INT %lu", (tmp & SDIO_INT) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_BUS_CTRL_REG, p, 1);
_PrintField("CD 0x%lX", (tmp & SDIO_CD) >> 7);
_PrintField("SCSI 0x%lX", (tmp & SDIO_SCSI) >> 6);
_PrintField("ECSI 0x%lX", (tmp & SDIO_ECSI) >> 5);
_PrintField("BUS_WIDTH 0x%lX", (tmp & SDIO_BUSWIDTH) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_CAP_REG, p, 1);
_PrintField("4BLS 0x%lX", (tmp & SDIO_4BLS) >> 7);
_PrintField("LSC 0x%lX", (tmp & SDIO_LSC) >> 6);
_PrintField("E4MI 0x%lX", (tmp & SDIO_E4MI) >> 5);
_PrintField("S4MI 0x%lX", (tmp & SDIO_S4MI) >> 4);
_PrintField("SBS 0x%lX", (tmp & SDIO_SBS) >> 3);
_PrintField("SRW 0x%lX", (tmp & SDIO_SRW) >> 2);
_PrintField("SMB 0x%lX", (tmp & SDIO_SMB) >> 1);
_PrintField("SDC 0x%lX", (tmp & SDIO_SDC) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_CIS_PTR_REG, p, 3);
_PrintField("CIS_PTR 0x%06lX", tmp);
addrCIS = tmp;
tmp = 0;
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_BUS_SUSP_REG, p, 1);
_PrintField("BR 0x%lX", (tmp & SDIO_BR) >> 1);
_PrintField("BS 0x%lX", (tmp & SDIO_BS) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_FUN_SEL_REG, p, 1);
_PrintField("DF 0x%lX", (tmp & SDIO_DF) >> 7);
_PrintField("FS 0x%lX", (tmp & SDIO_FS) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_EXEC_REG, p, 1);
_PrintField("EX 0x%lX", (tmp & SDIO_EX) >> 0);
_PrintField("EXM 0x%lX", (tmp & SDIO_EXM) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_READY_REG, p, 1);
_PrintField("RF 0x%lX", (tmp & SDIO_RF) >> 1);
_PrintField("RFM 0x%lX", (tmp & SDIO_RFM) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_FN0_BLKSIZ_REG, p, 2);
_PrintField("FN0_SIZE %lu", tmp);
tmp = 0;
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_POWER_REG, p, 1);
_PrintField("EMPC 0x%lX", (tmp & SDIO_EMPC) >> 1);
_PrintField("SMPC 0x%lX", (tmp & SDIO_SMPC) >> 0);
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_HS_REG, p, 1);
_PrintField("EHS 0x%lX", (tmp & SDIO_EHS) >> 1);
_PrintField("SHS 0x%lX", (tmp & SDIO_SHS) >> 0);
/* Metaformat */
SdioFindTuples(sdmmcp, addrCIS, 128, &addrManfID, &addrFuncE);
if (addrManfID != 0) {
SDIO_ReadDirect(sdmmcp, SDIO_CIA, addrManfID, buf, 6);
_PrintTitle("CISTPL_MANFID");
_PrintField("MANF 0x%04X", (uint16_t)buf[3] << 8 | buf[2]);
_PrintField("CARD 0x%04X", (uint16_t)buf[5] << 8 | buf[4]);
}
if (addrFuncE != 0) {
SDIO_ReadDirect(sdmmcp, SDIO_CIA, addrFuncE, buf, 6);
_PrintTitle("CISTPL_FUNCE Fun0");
_PrintField("BL_SIZE %u", (uint16_t)buf[4] << 8 | buf[3]);
_PrintField("MAX_TRAN_SPD 0x%02X", buf[5]);
}
/* I/O function 1 */
SDIO_ReadDirect(sdmmcp, SDIO_CIA, SDIO_FBR_ADDR(1, SDIO_FBR_CIS_PTR),
p, 3);
addrFuncE = 0;
/* TODO Augment SdioFindTuples so it finds CISTPL_FUNCE for Function 1
* with Extended Data 01h */
SdioFindTuples(sdmmcp, tmp, 256, NULL, &addrFuncE);
if (addrFuncE != 0) {
SDIO_ReadDirect(sdmmcp, SDIO_CIA, addrFuncE, buf, 16);
_PrintTitle("CISTPL_FUNCE Fun1");
_PrintField("MAX_BLK_SIZE %u", (uint16_t)buf[0xf] << 8
| buf[0xe]);
}
}
#endif

View File

@ -0,0 +1,295 @@
#ifndef CH_SDMMC_SDIO_H_
#define CH_SDMMC_SDIO_H_
/** \addtogroup sdio_api
* @{
*/
/*----------------------------------------------------------------------------
* Constants
*----------------------------------------------------------------------------*/
/** \addtogroup sdio_status SDIO Status bits
* @{
*/
#define SDIO_R1_IDLE (1ul << 0) /**< in idle state */
#define SDIO_R1_ILLEGAL_COMMAND (1ul << 2) /**< illegal command */
#define SDIO_R1_COM_CRC_ERROR (1ul << 3) /**< COM CRC error */
#define SDIO_R1_FUNCN_ERROR (1ul << 4) /**< Function number error */
#define SDIO_R1_PARAM_ERROR (1ul << 6) /**< Parameter error */
#define SDIO_R6_COM_CRC_ERROR (1ul << 15) /**< CRC check of command fails */
#define SDIO_R6_ILLEGAL_COMMAND (1ul << 14) /**< Command not legal for the state */
#define SDIO_R6_ERROR (1ul << 13) /**< General or unknown error */
#define SDIO_R5_Pos (8) /**< R5 starting position */
#define SDIO_R5_COM_CRC_ERROR (1ul << 15)
#define SDIO_R5_ILLEGAL_COMMAND (1ul << 14)
#define SDIO_R5_IO_STATE (3ul << 12) /**< DIS/CMD/TRN/RFU */
#define SDIO_R5_STATE_DIS (0ul << 12)
#define SDIO_R5_STATE_CMD (1ul << 12)
#define SDIO_R5_STATE_TRN (2ul << 12)
#define SDIO_R5_STATE_RFU (3ul << 12)
#define SDIO_R5_ERROR (1ul << 11)
#define SDIO_R5_FUNCN_ERROR (1ul << 9)
#define SDIO_R5_OUT_OF_RANGE (1ul << 8)
#define SDIO_R4_OCR (0xF << 0) /**< OCR */
#define SDIO_R4_MP (1ul << 27) /**< Memory Present */
#define SDIO_R4_NF (3ul << 28) /**< Number of Functions */
/** @}*/
/** \addtogroup sdio_fun_def SDIO Functions
* Here lists SDIO functions definitions
* - \ref SDIO_CIA or \ref SDIO_FN0
* - \ref SDIO_FN1
* - \ref SDIO_FN2
* - \ref SDIO_FN3
* - \ref SDIO_FN4
* - \ref SDIO_FN5
* - \ref SDIO_FN6
* - \ref SDIO_FN7
* @{*/
#define SDIO_CIA 0 /**< SDIO Function 0 (CIA) */
#define SDIO_FN0 0 /**< SDIO Function 0 */
#define SDIO_FN1 1 /**< SDIO Function 1 */
#define SDIO_FN2 2 /**< SDIO Function 2 */
#define SDIO_FN3 3 /**< SDIO Function 3 */
#define SDIO_FN4 4 /**< SDIO Function 4 */
#define SDIO_FN5 5 /**< SDIO Function 5 */
#define SDIO_FN6 6 /**< SDIO Function 6 */
#define SDIO_FN7 7 /**< SDIO Function 7 */
/** @}*/
/** \addtogroup sdio_cccr_def SDIO Card Common Control Registers (CCCR)
* Here lists SDIO CCCR definitions
* -# \ref SDIO_CCCR_REG
* -# \ref SDIO_SD_REV_REG
* -# \ref SDIO_IOE_REG
* -# \ref SDIO_IOR_REG
* -# \ref SDIO_IEN_REG
* -# \ref SDIO_INT_REG
* -# \ref SDIO_IOA_REG
* -# \ref SDIO_BUS_CTRL_REG
* -# \ref SDIO_CAP_REG
* -# \ref SDIO_CIS_PTR_REG
* -# .
* -# .
* -# \ref SDIO_BUS_SUSP_REG
* -# \ref SDIO_FUN_SEL_REG
* -# \ref SDIO_EXEC_REG
* -# \ref SDIO_READY_REG
* -# \ref SDIO_FN0_BLKSIZ_REG
* -# .
* -# \ref SDIO_POWER_REG
* -# \ref SDIO_HS_REG
* @{*/
#define SDIO_CCCR_REG 0x00 /**< CCCR/SDIO revision (RO) */
#define SDIO_CCCR (0xFUL << 0)/**< CCCR Format Version number */
#define SDIO_CCCR_1_00 (0x0UL << 0)/**< CCCR/FBR Version 1.00 */
#define SDIO_CCCR_1_10 (0x1UL << 0)/**< CCCR/FBR Version 1.10 */
#define SDIO_CCCR_1_20 (0x2UL << 0)/**< CCCR/FBR Version 1.20 */
#define SDIO_SDIO (0xFUL << 4)/**< SDIO Specification */
#define SDIO_SDIO_1_00 (0x0UL << 4)/**< SDIO Specification 1.00 */
#define SDIO_SDIO_1_10 (0x1UL << 4)/**< SDIO Specification 1.10 */
#define SDIO_SDIO_1_20 (0x2UL << 4)/**< SDIO Specification 1.20(unreleased) */
#define SDIO_SDIO_2_00 (0x3UL << 4)/**< SDIO Specification Version 2.00 */
#define SDIO_SD_REV_REG 0x01 /**< SD Specification Revision (RO) */
#define SDIO_SD (0xFUL << 0)/**< SD Physical Specification */
#define SDIO_SD_1_01 (0x0UL << 0)/**< SD 1.01 (Mar 2000) */
#define SDIO_SD_1_10 (0x1UL << 0)/**< SD 1.10 (Oct 2004) */
#define SDIO_SD_2_00 (0x2UL << 0)/**< SD 2.00 (May 2006) */
#define SDIO_IOE_REG 0x02 /**< I/O Enable (R/W) */
#define SDIO_IOE 0xFEUL /**< Enable/Disable Function */
#define SDIO_IOE_FN1 (0x1UL << 1)/**< Function 1 Enable/Disable */
#define SDIO_IOE_FN2 (0x1UL << 2)/**< Function 2 Enable/Disable */
#define SDIO_IOE_FN3 (0x1UL << 3)/**< Function 3 Enable/Disable */
#define SDIO_IOE_FN4 (0x1UL << 4)/**< Function 4 Enable/Disable */
#define SDIO_IOE_FN5 (0x1UL << 5)/**< Function 5 Enable/Disable */
#define SDIO_IOE_FN6 (0x1UL << 6)/**< Function 6 Enable/Disable */
#define SDIO_IOE_FN7 (0x1UL << 7)/**< Function 7 Enable/Disable */
#define SDIO_IOR_REG 0x03 /**< I/O Ready (RO) */
#define SDIO_IOR 0xFEUL /**< I/O Function Ready */
#define SDIO_IOR_FN1 (0x1UL << 1)/**< Function 1 ready */
#define SDIO_IOR_FN2 (0x1UL << 2)/**< Function 2 ready */
#define SDIO_IOR_FN3 (0x1UL << 3)/**< Function 3 ready */
#define SDIO_IOR_FN4 (0x1UL << 4)/**< Function 4 ready */
#define SDIO_IOR_FN5 (0x1UL << 5)/**< Function 5 ready */
#define SDIO_IOR_FN6 (0x1UL << 6)/**< Function 6 ready */
#define SDIO_IOR_FN7 (0x1UL << 7)/**< Function 7 ready */
#define SDIO_IEN_REG 0x04 /**< Int Enable */
#define SDIO_IENM 0x01UL /**< Int Enable Master (R/W) */
#define SDIO_IEN 0xFEUL /**< Int Enable for function (R/W) */
#define SDIO_IEN_FN1 (0x1UL << 1)/**< Function 1 Int Enable */
#define SDIO_IEN_FN2 (0x1UL << 2)/**< Function 2 Int Enable */
#define SDIO_IEN_FN3 (0x1UL << 3)/**< Function 3 Int Enable */
#define SDIO_IEN_FN4 (0x1UL << 4)/**< Function 4 Int Enable */
#define SDIO_IEN_FN5 (0x1UL << 5)/**< Function 5 Int Enable */
#define SDIO_IEN_FN6 (0x1UL << 6)/**< Function 6 Int Enable */
#define SDIO_IEN_FN7 (0x1UL << 7)/**< Function 7 Int Enable */
#define SDIO_INT_REG 0x05 /**< Int Pending */
#define SDIO_INT 0xFE /**< Int Pending for functions (RO) */
#define SDIO_INT_FN1 (0x1UL << 1)/**< Function 1 Int pending */
#define SDIO_INT_FN2 (0x1UL << 2)/**< Function 2 Int pending */
#define SDIO_INT_FN3 (0x1UL << 3)/**< Function 3 Int pending */
#define SDIO_INT_FN4 (0x1UL << 4)/**< Function 4 Int pending */
#define SDIO_INT_FN5 (0x1UL << 5)/**< Function 5 Int pending */
#define SDIO_INT_FN6 (0x1UL << 6)/**< Function 6 Int pending */
#define SDIO_INT_FN7 (0x1UL << 7)/**< Function 7 Int pending */
#define SDIO_IOA_REG 0x06 /**< I/O Abort */
#define SDIO_AS (0x7UL << 0)/**< Abort Select In Order (WO) */
#define SDIO_AS_FN1 (0x1UL << 0)/**< Abort function 1 IO */
#define SDIO_AS_FN2 (0x2UL << 0)/**< Abort function 2 IO */
#define SDIO_AS_FN3 (0x3UL << 0)/**< Abort function 3 IO */
#define SDIO_AS_FN4 (0x4UL << 0)/**< Abort function 4 IO */
#define SDIO_AS_FN5 (0x5UL << 0)/**< Abort function 5 IO */
#define SDIO_AS_FN6 (0x6UL << 0)/**< Abort function 6 IO */
#define SDIO_AS_FN7 (0x7UL << 0)/**< Abort function 7 IO */
#define SDIO_RES (0x1UL << 3)/**< IO CARD RESET (WO) */
#define SDIO_BUS_CTRL_REG 0x07 /**< Bus Interface Control */
#define SDIO_BUSWIDTH (0x3UL << 0)/**< Data bus width (R/W) */
#define SDIO_BUSWIDTH_1B (0x0UL << 0)/**< 1-bit data bus */
#define SDIO_BUSWIDTH_4B (0x2UL << 0)/**< 4-bit data bus */
#define SDIO_ECSI (0x1UL << 5)/**< Enable Continuous SPI interrupt (R/W) */
#define SDIO_SCSI (0x1UL << 6)/**< Support Continuous SPI interrupt (RO) */
#define SDIO_CD (0x1UL << 7)/**< Connect(0)/Disconnect(1) pull-up on CD/DAT[3] (R/W) */
#define SDIO_CAP_REG 0x08 /**< Card Capability */
#define SDIO_SDC (0x1UL << 0)/**< Support Direct Commands during data transfer (RO) */
#define SDIO_SMB (0x1UL << 1)/**< Support Multi-Block (RO) */
#define SDIO_SRW (0x1UL << 2)/**< Support Read Wait (RO) */
#define SDIO_SBS (0x1UL << 3)/**< Support Suspend/Resume (RO) */
#define SDIO_S4MI (0x1UL << 4)/**< Support interrupt between blocks of data in 4-bit SD mode (RO) */
#define SDIO_E4MI (0x1UL << 5)/**< Enable interrupt between blocks of data in 4-bit SD mode (R/W) */
#define SDIO_LSC (0x1UL << 6)/**< Low-Speed Card (RO) */
#define SDIO_4BLS (0x1UL << 7)/**< 4-bit support for Low-Speed Card (RO) */
#define SDIO_CIS_PTR_REG 0x09 /**< Pointer to CIS (3B, LSB first) */
#define SDIO_BUS_SUSP_REG 0x0C /**< Bus Suspend */
#define SDIO_BS (0x1UL << 0)/**< Bus Status (transfer on DAT[x] lines) (RO) */
#define SDIO_BR (0x1UL << 1)/**< Bus Release Request/Status (R/W) */
#define SDIO_FUN_SEL_REG 0x0D /**< Function select */
#define SDIO_DF (0x1UL << 7)/**< Resume Data Flag (RO) */
#define SDIO_FS (0xFUL << 0)/**< Select Function (R/W) */
#define SDIO_FS_CIA (0x0UL << 0)/**< Select CIA (function 0) */
#define SDIO_FS_FN1 (0x1UL << 0)/**< Select Function 1 */
#define SDIO_FS_FN2 (0x2UL << 0)/**< Select Function 2 */
#define SDIO_FS_FN3 (0x3UL << 0)/**< Select Function 3 */
#define SDIO_FS_FN4 (0x4UL << 0)/**< Select Function 4 */
#define SDIO_FS_FN5 (0x5UL << 0)/**< Select Function 5 */
#define SDIO_FS_FN6 (0x6UL << 0)/**< Select Function 6 */
#define SDIO_FS_FN7 (0x7UL << 0)/**< Select Function 7 */
#define SDIO_FS_MEM (0x8UL << 0)/**< Select memory in combo card */
#define SDIO_EXEC_REG 0x0E /**< Exec Flags (RO) */
#define SDIO_EXM (0x1UL << 0)/**< Executing status of memory */
#define SDIO_EX (0xFEUL) /**< Executing status of functions */
#define SDIO_EX_FN1 (0x1UL << 1)/**< Executing status of function 1 */
#define SDIO_EX_FN2 (0x1UL << 2)/**< Executing status of function 2 */
#define SDIO_EX_FN3 (0x1UL << 3)/**< Executing status of function 3 */
#define SDIO_EX_FN4 (0x1UL << 4)/**< Executing status of function 4 */
#define SDIO_EX_FN5 (0x1UL << 5)/**< Executing status of function 5 */
#define SDIO_EX_FN6 (0x1UL << 6)/**< Executing status of function 6 */
#define SDIO_EX_FN7 (0x1UL << 7)/**< Executing status of function 7 */
#define SDIO_READY_REG 0x0F /**< Ready Flags (RO) */
#define SDIO_RFM (0x1UL << 0)/**< Ready Flag for memory */
#define SDIO_RF (0xFEUL) /**< Ready Flag for functions */
#define SDIO_RF_FN1 (0x1UL << 1)/**< Ready Flag for function 1 */
#define SDIO_RF_FN2 (0x1UL << 2)/**< Ready Flag for function 2 */
#define SDIO_RF_FN3 (0x1UL << 3)/**< Ready Flag for function 3 */
#define SDIO_RF_FN4 (0x1UL << 4)/**< Ready Flag for function 4 */
#define SDIO_RF_FN5 (0x1UL << 5)/**< Ready Flag for function 5 */
#define SDIO_RF_FN6 (0x1UL << 6)/**< Ready Flag for function 6 */
#define SDIO_RF_FN7 (0x1UL << 7)/**< Ready Flag for function 7 */
#define SDIO_FN0_BLKSIZ_REG 0x10 /**< FN0 Block Size (2B, LSB first) (R/W) */
#define SDIO_POWER_REG 0x12 /**< Power Control */
#define SDIO_SMPC (0x1UL << 0)/**< Support Master Power Control (RO) */
#define SDIO_EMPC (0x1UL << 1)/**< Enable Master Power Control (R/W) */
#define SDIO_HS_REG 0x13 /**< High-Speed */
#define SDIO_SHS (0x1UL << 0)/**< Support High-Speed (RO) */
#define SDIO_EHS (0x1UL << 1)/**< Enable High-Speed (R/W) */
/** @}*/
/** \addtogroup sdio_fbr_def SDIO Function Basic Registers (FBR)
* Here lists SDIO Function Basic Register definitions.
* - SDIO_FBR_ADDR()
* -# \ref SDIO_FBR_CSA_IF
* -# \ref SDIO_FBR_EXT_IF
* -# \ref SDIO_FBR_PWR
* -# \ref SDIO_FBR_CIS_PTR
* -# .
* -# .
* -# \ref SDIO_FBR_CSA_PTR
* -# .
* -# .
* -# \ref SDIO_FBR_CSA_DATA
* -# \ref SDIO_FBR_BLK_SIZ
* -# .
* @{*/
#define SDIO_FBR_ADDR(fn, x) (0x100*(fn) + (x))
#define SDIO_FBR_CSA_IF 0x0 /**< CSA and function interface code (RO) */
#define SDIO_IFC (0xFUL << 0)/**< Standard SDIO Fun Interface Code */
#define SDIO_IFC_NO_IF (0x0UL << 0)/**< No SDIO standard interface */
#define SDIO_IFC_UART (0x1UL << 0)/**< UART */
#define SDIO_IFC_TA_BT (0x2UL << 0)/**< Type-A Bluetooth */
#define SDIO_IFC_TB_BT (0x3UL << 0)/**< Type-B Bluetooth */
#define SDIO_IFC_GPS (0x4UL << 0)/**< GPS */
#define SDIO_IFC_CAMERA (0x5UL << 0)/**< Camera */
#define SDIO_IFC_PHS (0x6UL << 0)/**< PHS */
#define SDIO_IFC_WLAN (0x7UL << 0)/**< WLAN */
#define SDIO_IFC_ATA (0x8UL << 0)/**< Embedded SDIO-ATA */
#define SDIO_IFC_EXT (0xFUL << 0)/**< Check EXT interface code */
#define SDIO_SCSA (0x1UL << 6)/**< Function supports Code Storage Area (CSA) */
#define SDIO_FBR_CSA (0x1UL << 7)/**< Function CSA enable */
#define SDIO_FBR_EXT_IF 0x1 /**< Extended function interface code (RO) */
#define SDIO_FBR_PWR 0x2 /**< function power control */
#define SDIO_SPS (0x1UL << 0)/**< function support power selection (RO) */
#define SDIO_EPS (0x1UL << 1)/**< Low Current Mode/High Current Mode (R/W) */
#define SDIO_FBR_CIS_PTR 0x9 /**< Address pointer to function CIS (3B, LSB first) (RO) */
#define SDIO_FBR_CSA_PTR 0xC /**< Address pointer to CSA (3B, LSB first) (R/W) */
#define SDIO_FBR_CSA_DATA 0xF /**< Read/Write fifo to CSA (R/W) */
#define SDIO_FBR_BLK_SIZ 0x10 /**< Block size (2B, LSB first) (R/W) */
/** @}*/
/** \addtogroup sdio_meta_def SDIO Card Metaformat
* Here lists definitions for SDIO metaformats.
* - \ref CISTPL_NULL
* - \ref CISTPL_DEVICE
* - \ref CISTPL_CHECKSUM
* - \ref CISTPL_VERS_1
* - \ref CISTPL_ALTSTR
* - \ref CISTPL_MANFID
* - \ref CISTPL_FUNCID
* - \ref CISTPL_FUNCE
* - \ref CISTPL_SDIO_STD
* - \ref CISTPL_SDIO_EXT
* - \ref CISTPL_END
* @{*/
#define CISTPL_NULL 0x00 /**< Null tuple (PCMCIA 3.1.9) */
#define CISTPL_DEVICE 0x01 /**< Device tuple (PCMCIA 3.2.2) */
#define CISTPL_CHECKSUM 0x10 /**< Checksum control (PCMCIA 3.1.1) */
#define CISTPL_VERS_1 0x15 /**< Level 1 version (PCMCIA 3.2.10) */
#define CISTPL_ALTSTR 0x16 /**< Alternate Language String (PCMCIA 3.2.1) */
#define CISTPL_MANFID 0x20 /**< Manufacturer Identification String (PCMCIA 3.2.9) */
#define CISTPL_FUNCID 0x21 /**< Function Identification (PCMCIA 3.2.7) */
#define CISTPL_FUNCE 0x22 /**< Function Extensions (PCMCIA 3.2.6) */
#define CISTPL_SDIO_STD 0x91 /**< Additional information for SDIO (PCMCIA 6.1.2) */
#define CISTPL_SDIO_EXT 0x92 /**< Reserved for future SDIO (PCMCIA 6.1.3) */
#define CISTPL_END 0xFF /**< The End-of-chain Tuple (PCMCIA 3.1.2) */
/** @}*/
/** Status bits mask for SDIO R5 */
#define STATUS_SDIO_R5 (0/*SDIO_R5_STATE*/ \
| SDIO_R5_ERROR \
| SDIO_R5_FUNCN_ERROR \
| SDIO_R5_OUT_OF_RANGE)
extern uint8_t SdioInit(SdmmcDriver *driver);
extern uint8_t SDIO_ReadDirect(SdmmcDriver *sdmmcp,
uint8_t functionNum,
uint32_t address, uint8_t * pData, uint32_t size);
extern uint32_t SdioGetMaxFreq(SdmmcDriver *sdmmcp);
extern void SDIO_DumpCardInformation(SdmmcDriver *sdmmcp);
extern void SDIO_DumpCardInformation(SdmmcDriver *sdmmcp);
#endif /* CH_SDMMC_SDIO_H_ */

View File

@ -0,0 +1,251 @@
#include "hal.h"
#include "sama_sdmmc_lld.h"
#include "ch_sdmmc_pmc.h"
#include "ch_sdmmc_tc.h"
/*------------------------------------------------------------------------------
* Global functions
*------------------------------------------------------------------------------*/
uint32_t get_tc_id_from_addr(const Tc* addr, uint8_t channel)
{
(void)channel;
#ifdef TC0
if (addr == TC0)
#ifdef ID_TC0_CH0
return ID_TC0 + channel;
#else
return ID_TC0;
#endif
#endif
#ifdef TC1
if (addr == TC1)
#ifdef ID_TC1_CH0
return ID_TC1 + channel;
#else
return ID_TC1;
#endif
#endif
#ifdef TC2
if (addr == TC2)
#ifdef ID_TC2_CH0
return ID_TC2 + channel;
#else
return ID_TC2;
#endif
#endif
#ifdef TC3
if (addr == TC3)
#ifdef ID_TC3_CH0
return ID_TC3 + channel;
#else
return ID_TC3;
#endif
#endif
return ID_PERIPH_COUNT;
}
void tc_configure(Tc *tc, uint32_t channel, uint32_t mode)
{
TcChannel *ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
/* Disable TC clock */
ch->TC_CCR = TC_CCR_CLKDIS;
/* Disable interrupts */
ch->TC_IDR = ch->TC_IMR;
/* Clear status register */
ch->TC_SR;
/* Set mode */
ch->TC_CMR = mode;
}
void tc_start(Tc *tc, uint32_t channel)
{
TcChannel *ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
/* Clear status register */
ch->TC_SR;
ch->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
}
void tc_stop(Tc *tc, uint32_t channel)
{
TcChannel *ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
ch->TC_CCR = TC_CCR_CLKDIS;
}
void tc_enable_it(Tc *tc, uint32_t channel, uint32_t mask)
{
TcChannel *ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
ch->TC_IER = mask;
}
void tc_disable_it(Tc *tc, uint32_t channel, uint32_t mask)
{
TcChannel *ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
ch->TC_IDR = mask;
}
uint32_t tc_find_best_clock_source(Tc *tc, uint8_t channel, uint32_t freq)
{
const int tcclks[] = {
TC_CMR_TCCLKS_TIMER_CLOCK1,
TC_CMR_TCCLKS_TIMER_CLOCK2,
TC_CMR_TCCLKS_TIMER_CLOCK3,
TC_CMR_TCCLKS_TIMER_CLOCK4,
TC_CMR_TCCLKS_TIMER_CLOCK5,
};
int i, best, higher;
int best_freq, higher_freq;
best = higher = -1;
best_freq = higher_freq = 0;
for (i = 0 ; i <(int) ARRAY_SIZE(tcclks) ; i++) {
uint32_t f = tc_get_available_freq(tc, channel, tcclks[i]);
if ( higher < 0 || (f > ((uint32_t)higher_freq) ) ) {
higher_freq = f;
higher = tcclks[i];
}
if (f > freq) {
if (best < 0 || (f - freq) < (f - best_freq)) {
best_freq = f;
best = tcclks[i];
}
}
}
if (best < 0)
best = higher;
return best;
}
uint32_t tc_get_status(Tc *tc, uint32_t channel)
{
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
return tc->TC_CHANNEL[channel].TC_SR;
}
uint32_t tc_get_available_freq(Tc *tc, uint8_t channel, uint8_t tc_clks)
{
uint32_t tc_id = get_tc_id_from_addr(tc, channel);
switch (tc_clks) {
case TC_CMR_TCCLKS_TIMER_CLOCK1:
#ifdef CONFIG_HAVE_PMC_GENERATED_CLOCKS
if (pmc_is_gck_enabled(tc_id))
return pmc_get_gck_clock(tc_id);
else
return 0;
#else
return pmc_get_peripheral_clock(tc_id) >> 1;
#endif
case TC_CMR_TCCLKS_TIMER_CLOCK2:
return pmc_get_peripheral_clock(tc_id) >> 3;
case TC_CMR_TCCLKS_TIMER_CLOCK3:
return pmc_get_peripheral_clock(tc_id) >> 5;
case TC_CMR_TCCLKS_TIMER_CLOCK4:
return pmc_get_peripheral_clock(tc_id) >> 7;
case TC_CMR_TCCLKS_TIMER_CLOCK5:
return pmc_get_slow_clock();
default:
return 0;
}
}
uint32_t tc_get_channel_freq(Tc *tc, uint32_t channel)
{
TcChannel* ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
return tc_get_available_freq(tc, channel, ch->TC_CMR & TC_CMR_TCCLKS_Msk);
}
void tc_set_ra_rb_rc(Tc *tc, uint32_t channel,
uint32_t *ra, uint32_t *rb, uint32_t *rc)
{
TcChannel* ch;
//assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
// assert(!(ra && rb) || (ch->TC_CMR & TC_CMR_WAVE));
if (ra)
ch->TC_RA = *ra;
if (rb)
ch->TC_RB = *rb;
if (rc)
ch->TC_RC = *rc;
}
void tc_get_ra_rb_rc(Tc *tc, uint32_t channel,
uint32_t *ra, uint32_t *rb, uint32_t *rc)
{
TcChannel* ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
if (ra)
*ra = ch->TC_RA;
if (rb)
*rb = ch->TC_RB;
if (rc)
*rc = ch->TC_RC;
}
#ifdef CONFIG_HAVE_TC_FAULT_MODE
void tc_set_fault_mode(Tc *tc, uint32_t mode)
{
tc->TC_FMR = mode;
}
#endif /* CONFIG_HAVE_TC_FAULT_MODE */
uint32_t tc_get_cv(Tc* tc, uint32_t channel)
{
TcChannel* ch;
// assert(channel < ARRAY_SIZE(tc->TC_CHANNEL));
ch = &tc->TC_CHANNEL[channel];
return ch->TC_CV;
}

View File

@ -0,0 +1,21 @@
#ifndef OS_HAL_PORTS_SAMA_LLD_SDMMCV0__SAMA_SDMMC_TC_H_
#define OS_HAL_PORTS_SAMA_LLD_SDMMCV0__SAMA_SDMMC_TC_H_
extern void tc_configure(Tc *tc, uint32_t channel, uint32_t mode);
extern void tc_start(Tc *tc, uint32_t channel);
extern void tc_stop(Tc *tc, uint32_t channel);
extern void tc_enable_it(Tc *tc, uint32_t channel, uint32_t mask);
extern void tc_disable_it(Tc *tc, uint32_t channel, uint32_t mask);
extern uint32_t tc_find_best_clock_source(Tc *tc, uint8_t channel, uint32_t freq);
extern uint32_t tc_get_status(Tc *tc, uint32_t channel);
extern uint32_t tc_get_available_freq(Tc *tc, uint8_t channel, uint8_t tc_clks);
extern uint32_t tc_get_channel_freq(Tc *tc, uint32_t channel);
extern void tc_set_ra_rb_rc(Tc *tc, uint32_t channel,uint32_t *ra, uint32_t *rb, uint32_t *rc);
extern void tc_get_ra_rb_rc(Tc *tc, uint32_t channel,uint32_t *ra, uint32_t *rb, uint32_t *rc);
extern uint32_t tc_get_cv(Tc* tc, uint32_t channel);
extern uint32_t get_tc_id_from_addr(const Tc* addr, uint8_t channel);
#endif /* OS_HAL_PORTS_SAMA_LLD_SDMMCV0__SAMA_SDMMC_TC_H_ */

View File

@ -0,0 +1,31 @@
#ifndef CH_SDMMC_TRACE_H_
#define CH_SDMMC_TRACE_H_
#if SAMA_SDMMC_TRACE == 1
#include "chprintf.h"
extern BaseSequentialStream * ts;
#define TRACE(s) chprintf(ts,s)
#define TRACE_1(s,v1) chprintf(ts,s,v1)
#define TRACE_2(s,v1,v2) chprintf(ts,s,v1,v2)
#define TRACE_3(s,v1,v2,v3) chprintf(ts,s,v1,v3)
#define TRACE_4(s,v1,v2,v3,v4) chprintf(ts,s,v1,v2,v3,v4)
#define TRACE_5(s,v1,v2,v3,v4,v5) chprintf(ts,s,v1,v2,v3,v4,v5)
#define TRACE_6(s,v1,v2,v3,v4,v5,v6) chprintf(ts,s,v1,v2,v3,v4,v5,v6)
#define TRACE_LEV_1(s,v1) TRACE_1(s,v1);
#else
#define TRACE(s)
#define TRACE_1(s,v1)
#define TRACE_2(s,v1,v2)
#define TRACE_3(s,v1,v2,v3)
#define TRACE_4(s,v1,v2,v3,v4)
#define TRACE_5(s,v1,v2,v3,v4,v5)
#define TRACE_6(s,v1,v2,v3,v4,v5,v6)
#define TRACE_LEV_1(s,v1)
#endif
#endif /* CH_SDMMC_TRACE_H_ */

View File

@ -0,0 +1,12 @@
PLATFORMSRC += $(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.c \
$(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.c
PLATFORMINC += $(CHIBIOS)/os/hal/ports/SAMA/LLD/SDMMCv1

View File

@ -0,0 +1,268 @@
/* CHIBIOS FIX */
#include "ch.h"
/*---------------------------------------------------------------------------/
/ FatFs - FAT file system module configuration file R0.12 (C)ChaN, 2016
/---------------------------------------------------------------------------*/
//#define _FFCONF 88100 /* Revision ID */
#define FFCONF_DEF 87030 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: All basic functions are enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_STRFUNC 0
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define FF_USE_FIND 0
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define FF_USE_MKFS 0
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_USE_FASTSEEK 0
/* This option switches fast seek function. (0:Disable or 1:Enable) */
#define FF_USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 0
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */
#define FF_USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define FF_USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable)
/ To enable it, also _FS_TINY need to be 1. */
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 850
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect setting of the code page can cause a file open failure.
/
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
*/
#define FF_USE_LFN 2
#define FF_MAX_LFN 255
/* The _USE_LFN switches the support of long file name (LFN).
/
/ 0: Disable support of LFN. _MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added
/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and
/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255.
/ It should be set 255 to support full featured LFN operations.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree(), must be added to the project. */
#define FF_LFN_UNICODE 0
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1.
/ This option also affects behavior of string I/O functions. */
#define FF_STRF_ENCODE 3
/* When _LFN_UNICODE == 1, this option selects the character encoding on the file to
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
/
/ 0: ANSI/OEM
/ 1: UTF-16LE
/ 2: UTF-16BE
/ 3: UTF-8
/
/ This option has no effect when _LFN_UNICODE == 0. */
#define FF_FS_RPATH 0
/* This option configures support of relative path.
/
/ 0: Disable relative path and remove related functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
*/
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES 2
/* Number of volumes (logical drives) to be used. */
#define FF_STR_VOLUME_ID 0
#define FF_VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
/* _STR_VOLUME_ID switches string support of volume ID.
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
/ the drive ID strings are: A-Z and 0-9. */
#define FF_MULTI_PARTITION 0
/* This option switches support of multi-partition on a physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When multi-partition is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* These options configure the range of sector size to be supported. (512, 1024,
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
/ disk_ioctl() function. */
#define FF_USE_TRIM 0
/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the file system object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 1
/* This option switches support of exFAT file system in addition to the traditional
/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled.
/ Note that enabling exFAT discards C89 compatibility. */
#define FF_FS_NORTC 1
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2016
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time.
/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to get current time form real-time clock. _NORTC_MON,
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (_FS_READONLY = 1). */
#define FF_FS_LOCK 0
/* The option _FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT MS2ST(1000)
#define FF_SYNC_t semaphore_t*
/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.c. */
/*--- End of configuration options ---*/

View File

@ -0,0 +1,25 @@
#ifndef SAMA_SDMMC_CONF_H
#define SAMA_SDMMC_CONF_H
#include "ch_sdmmc_sama5d2.h"
#include "ff.h"
typedef FATFS CH_SDMMC_FAT;
#ifndef SAMA_SDMMC_SDMMCDRIVER_IRQ_PRIORITY
#define SAMA_SDMMC_SDMMCDRIVER_IRQ_PRIORITY 4
#endif
#ifndef SAMA_SDMMC_TRACE
#define SAMA_SDMMC_TRACE 0
#endif
/** Default block size for SD/MMC access */
#ifndef SDMMC_BLOCK_SIZE
#define SDMMC_BLOCK_SIZE 512
#endif
#endif //SAMA_SDMMC_CONF_H

View File

@ -0,0 +1,378 @@
/*
ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file sama_sdmmc_lld.c
* @brief PLATFORM SDMMC driver
*
* @addtogroup SDMMC
* @{
*/
#include "hal.h"
#if (HAL_USE_SDMMC == TRUE) || defined(__DOXYGEN__)
#include <string.h>
#include "sama_sdmmc_lld.h"
#include "ch_sdmmc_device.h"
#include "ch_sdmmc_sd.h"
#include "ch_sdmmc_sdio.h"
#include "ch_sdmmc_trace.h"
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief SDMMC1 driver identifier.
*/
#if (PLATFORM_SDMMC_USE_SDMMC1 == TRUE) || defined(__DOXYGEN__)
SdmmcDriver SDMMCD1;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if (PLATFORM_SDMMC_USE_SDMMC1 == TRUE)
OSAL_IRQ_HANDLER(SAMA_SDMMCD1_HANDLER) {
OSAL_IRQ_PROLOGUE();
osalSysLockFromISR();
sdmmc_device_poll(&SDMMCD1);
osalSysUnlockFromISR();
aicAckInt();
OSAL_IRQ_EPILOGUE();
}
#endif
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level SDMMC driver initialization.
*
* @notapi
*/
void sdmmcInit(void)
{
#if PLATFORM_SDMMC_USE_SDMMC1 == TRUE
/* Driver initialization.*/
sdmmcObjectInit(&SDMMCD1);
#endif
}
/**
* @brief Configures and activates the SDMMC peripheral.
*
* @param[in] sdmmcp pointer to the @p SdmmcDriver object
*
* @notapi
*/
void sdmmcStart(SdmmcDriver *sdmmcp, const SamaSDMMCConfig *config)
{
uint8_t rc;
sdmmcp->config = config;
sdmmcp->card.EXT = sdmmcp->config->bp;
sdmmcp->card.SSR = &sdmmcp->config->bp[EXT_SIZE];
sdmmcp->card.SCR = &sdmmcp->config->bp[EXT_SIZE + SSR_SIZE];
sdmmcp->card.sandbox1 =&sdmmcp->config->bp[EXT_SIZE + SSR_SIZE + SCR_SIZE];
sdmmcp->card.sandbox2 = &sdmmcp->config->bp[EXT_SIZE + SSR_SIZE + SCR_SIZE + SB1_SIZE];
rc = sdmmc_device_lowlevelcfg(sdmmcp);
if (rc) {
//initialize
sdmmc_device_initialize(sdmmcp);
if (!sdmmcp->use_polling) {
#if (PLATFORM_SDMMC_USE_SDMMC1 == TRUE)
aicSetSourcePriority( (ID_SDMMC0 + sdmmcp->config->slot_id) ,SAMA_SDMMC_SDMMCDRIVER_IRQ_PRIORITY);
aicSetSourceHandler(ID_SDMMC0 + sdmmcp->config->slot_id,SAMA_SDMMCD1_HANDLER);
aicEnableInt( (ID_SDMMC0 + sdmmcp->config->slot_id) );
#endif
}
return;
}
TRACE_LEV_1("[%s] Cannot init board for MMC\r\n","ERROR");
sdmmcp->state = MCID_INIT_ERROR;
}
/**
* @brief Deactivates the SDMMC peripheral.
*
* @param[in] sdmmcp pointer to the @p SdmmcDriver object
*
* @notapi
*/
void sdmmcStop(SdmmcDriver *sdmmcp)
{
if (sdmmcp->state != MCID_OFF) {
/* Resets the peripheral.*/
/* Disables the peripheral.*/
aicDisableInt( (ID_SDMMC0 + sdmmcp->config->slot_id) );
if (sdmmcp->config->slot_id == SDMMC_SLOT0) {
pmcDisableSDMMC0();
} else if (sdmmcp->config->slot_id == SDMMC_SLOT1) {
pmcDisableSDMMC1();
}
}
}
/**
* @brief sends a command .
*
* @param[in] sdmmcp pointer to the @p SdmmcDriver object
*
* @notapi
*/
uint8_t sdmmcSendCmd(SdmmcDriver *sdmmcp)
{
uint32_t err;
uint8_t bRc;
if (sdmmcp->cmd.bCmd != 55) {
TRACE_2("Cmd%u(%lx)\n\r", sdmmcp->cmd.bCmd, sdmmcp->cmd.dwArg);
}
bRc = sdmmc_device_command(sdmmcp);
{
/* Poll command status.
* The driver is responsible for detecting and reporting
* timeout conditions. Here we only start a backup timer, in
* case the driver or the peripheral meets an unexpected
* condition. Mind that defining how long a command such as
* WRITE_MULTIPLE_BLOCK could take in total may only lead to an
* experimental value, lesser than the unrealistic theoretical
* maximum.
* Abort the command if the driver is still busy after 30s,
* which equals 30*1000 system ticks. */
sdmmcp->control_param = 1;
sdmmcp->timeout_elapsed = 0;
sdmmc_device_startTimeCount(sdmmcp);
do
{
err = sdmmc_device_control(sdmmcp, SDMMC_IOCTL_BUSY_CHECK);
sdmmc_device_checkTimeCount(sdmmcp);
}
while (sdmmcp->control_param && err == SDMMC_OK && !sdmmcp->timeout_elapsed);
if (err != SDMMC_OK) {
sdmmcp->cmd.bStatus = (uint8_t)err;
}
else if (sdmmcp->control_param) {
sdmmcp->control_param = 0;
sdmmc_device_control(sdmmcp, SDMMC_IOCTL_CANCEL_CMD);
sdmmcp->cmd.bStatus = SDMMC_NO_RESPONSE;
}
}
bRc = sdmmcp->cmd.bStatus;
//TRACE_1("post cmd bRc = %d\r\n",bRc);
if (bRc == SDMMC_CHANGED) {
//TRACE_2("Changed Cmd%u %s\n\r", sdmmcp->cmd.bCmd,SD_StringifyRetCode(bRc));
}
else if (bRc != SDMMC_OK) {
//TRACE_2("OK Cmd%u %s\n\r", sdmmcp->cmd.bCmd,SD_StringifyRetCode(bRc));
}
else if (sdmmcp->cmd.cmdOp.bmBits.respType == 1 && sdmmcp->cmd.pResp) {
//TRACE_2("Resp Cmd%u st %lx\n\r", sdmmcp->cmd.bCmd, *sdmmcp->cmd.pResp);
}
//TRACE_1("[ret sending cmd] %d\r\n",bRc);
return bRc;
}
void sdmmcObjectInit(SdmmcDriver *sdmmcp)
{
sdmmcp->state = MCID_OFF;
sdmmcp->timeout_elapsed = -1;
sdmmcp->config = NULL;
}
bool sdmmcOpenDevice(SdmmcDriver *sdmmcp)
{
uint8_t rc;
rc = sdmmc_device_start(sdmmcp);
if (rc != SDMMC_OK) {
TRACE_1("SD/MMC device initialization failed: %d\n\r", rc);
return false;
}
if (sdmmc_device_identify(sdmmcp) == SDMMC_OK) {
return false;
}
TRACE("SD/MMC device initialization successful\n\r");
return true;
}
bool sdmmcCloseDevice(SdmmcDriver *sdmmcp)
{
sdmmc_device_deInit(sdmmcp);
return true;
}
bool sdmmcShowDeviceInfo(SdmmcDriver *sdmmcp)
{
sSdCard *pSd =&sdmmcp->card;
TRACE("Show Device Info:\n\r");
#ifndef SDMMC_TRIM_INFO
const uint8_t card_type = sdmmcp->card.bCardType;
TRACE_1("Card Type: %d\n\r", card_type);
#endif
TRACE("Dumping Status ... \n\r");
SD_DumpStatus(pSd);
#ifndef SDMMC_TRIM_INFO
if (card_type & CARD_TYPE_bmSDMMC)
SD_DumpCID(pSd);
if (card_type & CARD_TYPE_bmSD) {
SD_DumpSCR(pSd->SCR);
SD_DumpSSR(pSd->SSR);
}
if (card_type & CARD_TYPE_bmSDMMC)
SD_DumpCSD(pSd);
#ifndef SDMMC_TRIM_MMC
if (card_type & CARD_TYPE_bmMMC)
SD_DumpExtCSD(pSd->EXT);
#endif
#ifndef SDMMC_TRIM_SDIO
if (card_type & CARD_TYPE_bmSDIO)
SDIO_DumpCardInformation(sdmmcp);
#endif
#endif
return true;
}
bool sdmmcMountVolume(SdmmcDriver *sdmmcp, CH_SDMMC_FAT *fs)
{
#if 0
//TODO to be done
const TCHAR drive_path[] = { '0' + sdmmcp->config->slot_id, ':', '\0' };
DIR dir = { .sect = 0 };
FILINFO fno = { 0 };
FRESULT res;
bool is_dir, rc = true;
(void)sdmmcp;
memset(fs, 0, sizeof(CH_SDMMC_FAT));
res = f_mount(fs, drive_path, 1);
if (res != FR_OK) {
TRACE_1("Failed to mount FAT file system, error %d\n\r", res);
return false;
}
res = f_opendir(&dir, drive_path);
if (res != FR_OK) {
TRACE_1("Failed to change to root directory, error %d\n\r", res);
return false;
} TRACE("Listing the files present in the root directory:\n\r");
for (;;) {
res = f_readdir(&dir, &fno);
if (res != FR_OK) {
TRACE_1("Error (%d) while listing files\n\r", res);
rc = false;
break;
}
if (fno.fname[0] == '\0')
break;
is_dir = fno.fattrib & AM_DIR ? true : false;
TRACE_3(" %s%s%c\n\r", is_dir ? "[" : "", fno.fname,is_dir ? ']' : ' ');
}
res = f_closedir(&dir);
if (res != FR_OK) {
TRACE_1("Failed to close directory, error %d\n\r", res);
rc = false;
}
return rc;
#else
(void)sdmmcp;
(void)fs;
return 0;
#endif
}
bool sdmmcUnmountVolume(SdmmcDriver *sdmmcp)
{
#if 0
//TODO to be done
const TCHAR drive_path[] = { '0' + sdmmcp->config->slot_id, ':', '\0' };
FRESULT res;
bool rc = true;
res = f_mount(NULL, drive_path, 0);
if (res != FR_OK)
rc = false;
return rc;
#else
(void)sdmmcp;
return 0;
#endif
}
#endif /* HAL_USE_SDMMC == TRUE */
/** @} */

View File

@ -0,0 +1,174 @@
/*
ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file sama_sdmmc_lld.h
* @brief PLATFORM SDMMC subsystem low level driver header.
*
* @addtogroup SDMMC
* @{
*/
#ifndef SAMA_SDMMC_LLD_H
#define SAMA_SDMMC_LLD_H
#if (HAL_USE_SDMMC == TRUE) || defined(__DOXYGEN__)
#include "ch_sdmmc.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name PLATFORM configuration options
* @{
*/
/**
* @brief PLATFORM_SDMMC_USE_SDMMC1 driver enable switch.
* @details If set to @p TRUE the support for PLATFORM_SDMMC_USE_SDMMC1 is included.
* @note The default is @p FALSE.
*/
#if !defined(PLATFORM_SDMMC_USE_SDMMC1) || defined(__DOXYGEN__)
#define PLATFORM_SDMMC_USE_SDMMC1 FALSE
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
typedef enum {
MCID_OFF, /**< Device not powered */
MCID_IDLE, /**< Idle */
MCID_LOCKED, /**< Locked for specific slot */
MCID_CMD, /**< Processing the command */
MCID_ERROR, /**< Command error */
MCID_INIT_ERROR
}sdmmcstate_t;
typedef struct {
sdmmcslots_t slot_id;
struct _pmc_periph_cfg pmccfg;
bool use_fastest_clock;
Tc * tctimer;
uint8_t tc_chan;
uint8_t * bp;
uint8_t * data_buf;
uint32_t data_buf_size;
uint32_t * dma_table;
uint32_t dma_table_size;
} SamaSDMMCConfig;
struct SamaSDMMCDriver
{
sdmmcstate_t state;
const SamaSDMMCConfig *config;
Sdmmc * regs; /* set of SDMMC hardware registers */
uint32_t tctimer_id; /* Timer/Counter peripheral ID (ID_TCx) */
uint32_t dev_freq; /* frequency of clock provided to memory
* device, in Hz */
sSdmmcCommand cmd;
sSdCard card;
uint16_t blk_index; /* count of data blocks tranferred already,
* in the context of the command and data
* transfer being executed */
uint8_t resp_len; /* size of the response, once retrieved,
* in the context of the command being
* executed, expressed in 32-bit words */
uint8_t tim_mode; /* timing mode aka bus speed mode */
uint16_t blk_size; /* max data block size, in bytes */
bool use_polling; /* polling mode */
bool cmd_line_released; /* handled the Command Complete event */
bool dat_lines_released; /* handled the Transfer Complete event */
bool expect_auto_end; /* waiting for completion of Auto CMD12 */
bool use_set_blk_cnt; /* implicit SET_BLOCK_COUNT command */
uint32_t control_param;
uint32_t timeout_ticks;
int8_t timeout_elapsed;
systime_t time,now;
};
typedef sSdCard sdmmclib;
typedef struct SamaSDMMCDriver SdmmcDriver;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if (PLATFORM_SDMMC_USE_SDMMC1 == TRUE)
extern SdmmcDriver SDMMCD1;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void sdmmcInit(void);
void sdmmcObjectInit(SdmmcDriver *sdmmcp);
void sdmmcStart(SdmmcDriver *sdmmcp, const SamaSDMMCConfig *config);
void sdmmcStop(SdmmcDriver *sdmmcp);
uint8_t sdmmcSendCmd(SdmmcDriver *sdmmcp);
bool sdmmcOpenDevice(SdmmcDriver *sdmmcp);
bool sdmmcCloseDevice(SdmmcDriver *sdmmcp);
bool sdmmcShowDeviceInfo(SdmmcDriver *sdmmcp);
bool sdmmcMountVolume(SdmmcDriver *sdmmcp,CH_SDMMC_FAT *fs);
bool sdmmcUnmountVolume(SdmmcDriver *sdmmcp);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_SDMMC == TRUE */
#endif /* SAMA_SDMMC_LLD_H */
/** @} */