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:
parent
6b741c1eb4
commit
5a733a0d43
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
@ -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
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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
|
|
@ -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_ */
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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_ */
|
|
@ -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
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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
|
||||
|
|
@ -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 ---*/
|
|
@ -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
|
||||
|
|
@ -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 */
|
||||
|
||||
/** @} */
|
|
@ -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 */
|
||||
|
||||
/** @} */
|
Loading…
Reference in New Issue