aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal
diff options
context:
space:
mode:
authorareviu <areviu.info@gmail.com>2018-01-07 11:28:07 +0000
committerareviu <areviu.info@gmail.com>2018-01-07 11:28:07 +0000
commit5a733a0d43e0b37f6ff63bab0f3c3084e8b219cb (patch)
treed2ecbd60f3ac9646abb7f2770836c79a27cdc568 /os/hal
parent6b741c1eb4e8c25d1421ff1abe61b57341a3c8fd (diff)
downloadChibiOS-5a733a0d43e0b37f6ff63bab0f3c3084e8b219cb.tar.gz
ChibiOS-5a733a0d43e0b37f6ff63bab0f3c3084e8b219cb.tar.bz2
ChibiOS-5a733a0d43e0b37f6ff63bab0f3c3084e8b219cb.zip
first draft. initialization working
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@11231 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/hal')
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.c499
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.h179
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.c1040
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.h207
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.c1994
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.h392
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_macros.h29
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.c375
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.h8
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.c830
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.h50
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sama5d2.h74
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.c781
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.h111
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.c288
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.h295
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.c251
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.h21
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_trace.h31
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/driver.mk12
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/ffconf.h268
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_conf.h25
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.c378
-rw-r--r--os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.h174
24 files changed, 8312 insertions, 0 deletions
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.c
new file mode 100644
index 000000000..04f2a619e
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.c
@@ -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;
+}
+
+
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.h
new file mode 100644
index 000000000..8b03a8bb8
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc.h
@@ -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*/
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.c
new file mode 100644
index 000000000..246a9f1ab
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.c
@@ -0,0 +1,1040 @@
+#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"
+
+
+/** sdmmc_opc_acc SD_SEND_OP_COND command argument fields
+ */
+#define SD_OPC_S18R (1ul << 24) /**< Switching to 1.8V signaling level Request */
+#define SD_OPC_XPC (1ul << 28) /**< SDXC Power Control */
+#define SD_OPC_FB (1ul << 29) /**< eSD Fast Boot */
+#define SD_OPC_HCS (1ul << 30) /**< Host Capacity Support */
+/**
+ * sdmmc_cmd_op SD/MMC Command Operations
+ */
+#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 */
+
+#define SDMMC_CMD_bmCRC (0x1 << 7) /**< CRC is enabled (SPI only) */
+#define SDMMC_CMD_bmOD (0x1 << 8) /**< Open-Drain is enabled (MMC) */
+#define SDMMC_CMD_bmIO (0x1 << 9) /**< IO function */
+#define SDMMC_CMD_bmBUSY (0x1 << 10) /**< Do busy check */
+
+/** Cmd: Do power on initialize */
+#define SDMMC_CMD_POWERONINIT (SDMMC_CMD_bmPOWERON)
+/** Cmd: Data only, read */
+#define SDMMC_CMD_DATARX (SDMMC_CMD_bmDATARX)
+/** Cmd: Data only, write */
+#define SDMMC_CMD_DATATX (SDMMC_CMD_bmDATATX)
+/** Cmd: Command without data */
+#define SDMMC_CMD_CNODATA(R) ( SDMMC_CMD_bmCOMMAND \
+ | SDMMC_CMD_bmRESP(R) )
+/** Cmd: Command with data, read */
+#define SDMMC_CMD_CDATARX(R) ( SDMMC_CMD_bmCOMMAND \
+ | SDMMC_CMD_bmDATARX \
+ | SDMMC_CMD_bmRESP(R) )
+/** Cmd: Command with data, write */
+#define SDMMC_CMD_CDATATX(R) ( SDMMC_CMD_bmCOMMAND \
+ | SDMMC_CMD_bmDATATX \
+ | SDMMC_CMD_bmRESP(R) )
+/** Cmd: Send Stop command */
+#define SDMMC_CMD_CSTOP ( SDMMC_CMD_bmCOMMAND \
+ | SDMMC_CMD_bmSTOPXFR \
+ | SDMMC_CMD_bmRESP(1) )
+/** Cmd: Send Stop token for SPI */
+#define SDMMC_CMD_STOPTOKEN (SDMMC_CMD_bmSTOPXFR)
+
+
+#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 ))
+
+
+static void _ResetCmd(sSdmmcCommand * pCmd);
+
+/**
+ * Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp
+ * up time.
+ * Returns the command transfer result (see SendMciCommand).
+ */
+uint8_t CmdPowerOn(SdmmcDriver *drv)
+{
+ //sSdmmcCommand *pCmd = &pSd->sdCmd;
+ uint8_t bRc;
+
+ TRACE("PwrON\n\r");
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_POWERONINIT;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+
+
+ return bRc;
+}
+
+
+
+
+
+/**
+ * Resets all cards to idle state
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param arg Argument used.
+ * \return the command transfer result (see SendMciCommand).
+ */
+uint8_t Cmd0(SdmmcDriver *drv, uint8_t arg)
+{
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(0);
+ drv->cmd.dwArg = arg;
+ drv->timeout_elapsed = -1;
+ bRc = sdmmcSendCmd(drv);
+
+
+ return bRc;
+}
+
+/**
+ * MMC send operation condition command.
+ * Sends host capacity support information and activates the card's
+ * initialization process.
+ * Returns the command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param hc Upon success tells whether the device is a high capacity device.
+ */
+#ifndef SDMMC_TRIM_MMC
+uint8_t Cmd1(SdmmcDriver *drv, bool * hc)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint32_t arg, ocr = 0;
+ uint8_t rc;
+ //int8_t elapsed = -1;
+
+ /* Tell the device that the host supports 512-byte sector addressing */
+ arg = MMC_OCR_ACCESS_SECTOR;
+ /* Tell the MMC device which voltage the host supplies to the VDD line
+ * (MMC card) or VCC line (e.MMC device).
+ * TODO get this board-specific value from platform code. On the
+ * SAMA5D2-XULT board, VDD is 3.3V ± 1%. */
+ arg |= SD_OCR_VDD_32_33 | SD_OCR_VDD_33_34;
+
+ /* Fill command */
+ _ResetCmd(&drv->cmd);
+
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(3) | SDMMC_CMD_bmOD;
+ pCmd->bCmd = 1;
+ pCmd->dwArg = arg;
+ pCmd->pResp = &ocr;
+
+ drv->timeout_ticks = TIME_S2I(1);
+ drv->timeout_elapsed = 0;
+ do {
+ rc = sdmmcSendCmd(drv);
+
+ if (rc == SDMMC_OK && !(ocr & SD_OCR_BUSYN))
+ rc = SDMMC_BUSY;
+
+ }
+ while (rc == SDMMC_BUSY && !drv->timeout_elapsed);
+
+ if (rc != SDMMC_OK)
+ return rc;
+
+ /* Analyze the final contents of the OCR Register */
+#if 0
+ TRACE("Device supports%s%s 3.0V:[%c%c%c%c%c%c]"
+ " 3.3V:[%c%c%c%c%c%c]\n\r",
+ ocr & MMC_OCR_VDD_170_195 ? " 1.8V" : "",
+ ocr & MMC_OCR_VDD_200_270 ? " 2.xV" : "",
+ ocr & SD_OCR_VDD_27_28 ? 'X' : '.',
+ ocr & SD_OCR_VDD_28_29 ? 'X' : '.',
+ ocr & SD_OCR_VDD_29_30 ? 'X' : '.',
+ ocr & SD_OCR_VDD_30_31 ? 'X' : '.',
+ ocr & SD_OCR_VDD_31_32 ? 'X' : '.',
+ ocr & SD_OCR_VDD_32_33 ? 'X' : '.',
+ ocr & SD_OCR_VDD_30_31 ? 'X' : '.',
+ ocr & SD_OCR_VDD_31_32 ? 'X' : '.',
+ ocr & SD_OCR_VDD_32_33 ? 'X' : '.',
+ ocr & SD_OCR_VDD_33_34 ? 'X' : '.',
+ ocr & SD_OCR_VDD_34_35 ? 'X' : '.',
+ ocr & SD_OCR_VDD_35_36 ? 'X' : '.');
+#endif
+ TRACE_1("Device access 0x%lx\n\r", ocr >> 29 & 0x3ul);
+
+ *hc = (ocr & MMC_OCR_ACCESS_MODE) == MMC_OCR_ACCESS_SECTOR? true : false;
+
+ return SDMMC_OK;
+}
+#endif
+
+/**
+ * Asks any card to send the CID numbers
+ * on the CMD line (any card that is
+ * connected to the host will respond)
+ * Returns the command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ */
+uint8_t Cmd2(SdmmcDriver *drv)
+{
+ //sSdmmcCommand *pCmd = &pSd->sdCmd;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(2) | SDMMC_CMD_bmOD;
+ drv->cmd.bCmd = 2;
+ drv->cmd.pResp = drv->card.CID;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+
+ return bRc;
+}
+
+/**
+ * Switches the mode of operation of the selected card.
+ * CMD6 is valid under the "trans" state.
+ * \return The command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pSwitchArg Pointer to the SWITCH_FUNC command argument.
+ * \param pStatus Pointer to where the 512bit status is returned.
+ * The buffer shall follow the peripheral and DMA alignment requirements.
+ * \param pResp Pointer to where the response is returned.
+ */
+uint8_t SdCmd6(SdmmcDriver *drv, const SdCmd6Arg * pSwitchArg, uint8_t * pStatus, uint32_t * pResp)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t bRc;
+
+ //assert(pSd);
+ //assert(pSwitchArg);
+
+ _ResetCmd(&drv->cmd);
+
+ pCmd->bCmd = 6;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
+
+ pCmd->dwArg = (pSwitchArg->set << 31)
+ | (pSwitchArg->reserved << 30)
+ | (pSwitchArg->func_grp6 << 20)
+ | (pSwitchArg->func_grp5 << 16)
+ | (pSwitchArg->pwr_limit << 12)
+ | (pSwitchArg->drv_strgth << 8)
+ | (pSwitchArg->cmd_sys << 4)
+ | (pSwitchArg->acc_mode << 0);
+
+ if (pStatus) {
+ pCmd->wBlockSize = 512 / 8;
+ pCmd->wNbBlocks = 1;
+ pCmd->pData = pStatus;
+ }
+ pCmd->pResp = pResp;
+ drv->timeout_elapsed = -1;
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+
+/**
+ * Ask the SD card to publish a new relative address (RCA)
+ * or
+ * Assign relative address to the MMC card
+ * Returns the command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pRsp Pointer to buffer to fill response (address on 31:16).
+ */
+/**
+ * Sends SD Memory Card interface condition, which includes host supply
+ * voltage information and asks the card whether card supports voltage.
+ * Should be performed at initialization time to detect the card type.
+ * \param pSd Pointer to a SD card driver instance.
+ * \param supplyVoltage Expected supply voltage(SD).
+ * \return 0 if successful;
+ * otherwise returns SD_ERROR_NORESPONSE if the card did not answer
+ * the command, or SDMMC_ERROR.
+ */
+uint8_t SdCmd8(SdmmcDriver *drv, uint8_t supplyVoltage)
+{
+
+ const uint32_t arg = (supplyVoltage << SD_IFC_VHS_Pos) | SD_IFC_CHK_PATTERN_STD;
+ uint32_t dwResp = 0;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command information */
+ drv->cmd.bCmd = 8;
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(7) | SDMMC_CMD_bmOD;
+ drv->cmd.dwArg = arg;
+ drv->cmd.pResp = &dwResp;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+
+ /* Expect the R7 response, which is the card interface condition.
+ * Expect VCA to match VHS, and the check pattern to match as well. */
+ if (bRc == SDMMC_OK && (dwResp & (SD_IFC_VHS_Msk | SD_IFC_CHK_PATTERN_Msk)) == arg)
+ return SDMMC_OK;
+ else if (bRc == SDMMC_NO_RESPONSE)
+ return SDMMC_NO_RESPONSE;
+ else
+ return SDMMC_ERR;
+}
+
+/**
+ * Command toggles a card between the
+ * stand-by and transfer states or between
+ * the programming and disconnect states.
+ * Returns the command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param cardAddr Relative Card Address (0 deselects all).
+ */
+uint8_t Cmd3(SdmmcDriver *drv)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint32_t dwResp;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.bCmd = 3;
+ drv->cmd.pResp = &dwResp;
+
+#ifndef SDMMC_TRIM_MMC
+ if (drv->card.bCardType == CARD_MMC || drv->card.bCardType == CARD_MMCHD) {
+ uint16_t wNewAddr = (uint16_t)max_u32(( drv->card.wAddress + 1) & 0xffff, 2);
+ pCmd->dwArg = wNewAddr << 16;
+
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1) | SDMMC_CMD_bmOD;
+
+ drv->timeout_elapsed = -1;
+ bRc = sdmmcSendCmd(drv);
+ if (bRc == SDMMC_OK) {
+ drv->card.wAddress = wNewAddr;
+ }
+ }
+ else
+#endif
+ {
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(6) | SDMMC_CMD_bmOD;
+ drv->timeout_elapsed = -1;
+ bRc = sdmmcSendCmd(drv);
+
+ if (bRc == SDMMC_OK) {
+ drv->card.wAddress = dwResp >> 16;
+ }
+ }
+ return bRc;
+}
+
+uint8_t Cmd7(SdmmcDriver *drv, uint16_t address)
+{
+ //sSdmmcCommand *pCmd = &pSd->sdCmd;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ /* If this function is used to transition the MMC device from the
+ * Disconnected to Programming state, then busy checking is required */
+ if (address)
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(1) | SDMMC_CMD_bmBUSY;
+ else
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(0);
+ drv->cmd.bCmd = 7;
+ drv->cmd.dwArg = address << 16;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+
+uint8_t Cmd9(SdmmcDriver *drv)
+{
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(2);
+ drv->cmd.bCmd = 9;
+ drv->cmd.dwArg = drv->card.wAddress << 16;
+ drv->cmd.pResp = drv->card.CSD;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+
+uint8_t Cmd11(SdmmcDriver *drv, uint32_t * pStatus)
+{
+ //sSdmmcCommand *pCmd = &pSd->sdCmd;
+
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.bCmd = 11;
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(1);
+ drv->cmd.dwArg = 0;
+ drv->cmd.pResp = pStatus;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+
+ return bRc;
+}
+
+/**
+ * Addressed card sends its status register.
+ * Returns the command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pStatus Pointer to a status variable.
+ */
+uint8_t Cmd13(SdmmcDriver *drv, uint32_t * pStatus)
+{
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.bCmd = 13;
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(1);
+ drv->cmd.dwArg = drv->card.wAddress << 16;
+ drv->cmd.pResp = pStatus;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+
+
+/**
+ * A host reads the reversed bus testing data pattern from a card
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pData Pointer to the buffer to be filled.
+ * The buffer shall follow the peripheral and DMA alignment requirements.
+ * \param len Length of data in byte
+ * \param pStatus Pointer response buffer as status return.
+ */
+#ifndef SDMMC_TRIM_MMC
+uint8_t Cmd14(SdmmcDriver *drv, uint8_t * pData, uint8_t len, uint32_t * pStatus)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
+ pCmd->bCmd = 14;
+ pCmd->pResp = pStatus;
+ pCmd->wBlockSize = len;
+ pCmd->wNbBlocks = 1;
+ pCmd->pData = pData;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+/**
+ * A host sends the bus test data pattern to a card.
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pData Pointer to the buffer to be filled.
+ * The buffer shall follow the peripheral and DMA alignment requirements.
+ * \param len Length of data in byte
+ * \param pStatus Pointer response buffer as status return.
+*/
+ uint8_t Cmd19(SdmmcDriver *drv, uint8_t * pData, uint8_t len, uint32_t * pStatus)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ pCmd->cmdOp.wVal = SDMMC_CMD_CDATATX(1);
+ pCmd->bCmd = 19;
+ pCmd->pResp = pStatus;
+ pCmd->wBlockSize = len;
+ pCmd->wNbBlocks = 1;
+ pCmd->pData = pData;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+#endif
+
+uint8_t Cmd16(SdmmcDriver *drv, uint16_t blkLen)
+{
+ //sSdmmcCommand *pCmd = &pSd->sdCmd;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ drv->cmd.cmdOp.wVal = SDMMC_CMD_CNODATA(1);
+ drv->cmd.bCmd = 16;
+ drv->cmd.dwArg = blkLen;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+
+ return bRc;
+}
+
+/**
+ * SDIO IO_RW_DIRECT command, response R5.
+ * \return the command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pIoData Pointer to input argument (\ref SdioRwDirectArg) and
+ * response (\ref SdmmcR5) buffer.
+ * \param fCallback Pointer to optional callback invoked on command end.
+ * NULL: Function return until command finished.
+ * Pointer: Return immediately and invoke callback at end.
+ * Callback argument is fixed to a pointer to sSdCard instance.
+ */
+uint8_t Cmd52(SdmmcDriver *drv,
+ uint8_t wrFlag,
+ uint8_t funcNb, uint8_t rdAfterWr, uint32_t addr, uint32_t * pIoData)
+{
+ SdioCmd52Arg *pArg52 = (SdioCmd52Arg *) pIoData;
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t bRc;
+
+ pArg52->rwFlag = wrFlag;
+ pArg52->functionNum = funcNb;
+ pArg52->rawFlag = rdAfterWr;
+ pArg52->regAddress = addr;
+
+ _ResetCmd(&drv->cmd);
+ /* Fill command */
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(5) | SDMMC_CMD_bmIO;
+ pCmd->bCmd = 52;
+ pCmd->dwArg = *pIoData;
+ pCmd->pResp = pIoData;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+/**
+ * Indicates to the card that the next command is an application specific
+ * command rather than a standard command.
+ * \return the command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param cardAddr Card Relative Address.
+ */
+uint8_t Cmd55(SdmmcDriver *drv, uint16_t cardAddr)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint32_t dwResp;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command information */
+ pCmd->bCmd = 55;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1)| (cardAddr ? 0 : SDMMC_CMD_bmOD);
+ pCmd->dwArg = cardAddr << 16;
+ pCmd->pResp = &dwResp;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+
+
+/**
+ * Defines the data bus width (00=1bit or 10=4 bits bus) to be used for data
+ * transfer.
+ * The allowed data bus widths are given in SCR register.
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param busWidth Bus width in bits (4 or 1).
+ * \return the command transfer result (see SendCommand).
+ */
+uint8_t Acmd6(SdmmcDriver *drv, uint8_t busWidth)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t error;
+
+ TRACE_1("Acmd%u\n\r", 6);
+
+ error = Cmd55(drv, drv->card.wAddress);
+
+ if (!error) {
+ _ResetCmd(&drv->cmd);
+
+ pCmd->bCmd = 6;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1);
+ pCmd->dwArg = (busWidth == 4) ? SD_SSR_DATA_BUS_WIDTH_4BIT : SD_SSR_DATA_BUS_WIDTH_1BIT;
+ drv->timeout_elapsed = -1;
+ error = sdmmcSendCmd(drv);
+ }
+ else {
+ if (error) {
+ TRACE_2("Acmd%u %s\n\r", 6, SD_StringifyRetCode(error));
+ }
+ }
+ return error;
+}
+
+
+/**
+ * From the selected card get its SD Status Register (SSR).
+ * ACMD13 is valid under the Transfer state.
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pSSR Pointer to a 64-byte buffer receiving the contents of the SSR.
+ * The buffer shall follow the peripheral and DMA alignment requirements.
+ * \param pResp Pointer to where the response is returned.
+ * \return The command transfer result (see SendCommand).
+ */
+uint8_t Acmd13(SdmmcDriver *drv, uint8_t * pSSR, uint32_t * pResp)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t error;
+
+ //assert(pSd);
+
+ TRACE_1("Acmd%u\n\r", 13);
+
+ error = Cmd55(drv, drv->card.wAddress);
+
+ if (!error) {
+
+ _ResetCmd(&drv->cmd);
+
+ pCmd->bCmd = 13;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
+ if (pSSR) {
+ pCmd->wBlockSize = 512 / 8;
+ pCmd->wNbBlocks = 1;
+ pCmd->pData = pSSR;
+ }
+ pCmd->pResp = pResp;
+ drv->timeout_elapsed = -1;
+ error = sdmmcSendCmd(drv);
+
+ } else {
+ if (error) {
+ TRACE_2("Acmd%u %s\n\r", 13, SD_StringifyRetCode(error));
+ }
+ }
+ return error;
+}
+
+/**
+ * Asks to all cards to send their operations conditions.
+ * Returns the command transfer result (see SendCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param low_sig_lvl In: tells whether the host supports UHS-I timing modes.
+ * Out: tells whether the device may switch to low signaling level.
+ * \param hc In: tells whether the device has replied to SEND_IF_COND.
+ * Out: tells whether the device is a high capacity device.
+ */
+uint8_t Acmd41(SdmmcDriver *drv, bool * low_sig_lvl, bool * hc)
+{
+
+ sSdmmcCommand *pCmd = &drv->cmd;
+ /* TODO get this board-specific value from platform code. On the
+ * SAMA5D2-XULT board, VDD is 3.3V ± 1%. */
+ const uint32_t vdd_range = SD_OCR_VDD_32_33 | SD_OCR_VDD_33_34;
+ uint32_t arg, ocr = 0;
+ uint8_t rc;
+ //int8_t elapsed = -1;
+
+ //trace_debug("Acmd%u\n\r", 41);
+ /* Provided the device has answered the SEND_IF_COND command, raise the
+ * Host Capacity Support flag. Also, set the SDXC Power Control flag.
+ * TODO assign XPC depending on board capabilities. */
+ arg = *hc ? SD_OPC_HCS | SD_OPC_XPC : 0;
+ /* Preparing UHS-I timing modes, ask the device whether it's in a
+ * position to switch to low signaling voltage. */
+ arg |= *low_sig_lvl ? SD_OPC_S18R : 0;
+ /* Tell the SD device which voltage the host supplies to the VDD line */
+ arg |= vdd_range;
+
+
+ drv->timeout_ticks = TIME_S2I(1);
+ drv->timeout_elapsed = 0;
+
+ do {
+ rc = Cmd55(drv, 0);
+
+ if (rc != SDMMC_OK)
+ break;
+
+ _ResetCmd(&drv->cmd);
+
+ pCmd->bCmd = 41;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(3);
+ pCmd->dwArg = arg;
+ pCmd->pResp = &ocr;
+ drv->timeout_elapsed = -1;
+ rc = sdmmcSendCmd(drv);
+
+ if (rc != SDMMC_OK)
+ break;
+
+
+ } while (!(ocr & SD_OCR_BUSYN) && !drv->timeout_elapsed);
+
+ if (!(ocr & SD_OCR_BUSYN) && rc == SDMMC_OK)
+ /* Supply voltage range is incompatible */
+ rc = SDMMC_BUSY;
+ if (rc != SDMMC_OK) {
+ TRACE_2("Acmd%u %s\n\r", 41, SD_StringifyRetCode(rc));
+ }
+ else {
+#if 0
+ /* Analyze the final contents of the OCR Register */
+ trace_info("Device supports%s%s 3.0V:[%c%c%c%c%c%c]"
+ " 3.3V:[%c%c%c%c%c%c]\n\r",
+ ocr & SD_OCR_VDD_LOW ? " 1.xV" : "", "",
+ ocr & SD_OCR_VDD_27_28 ? 'X' : '.',
+ ocr & SD_OCR_VDD_28_29 ? 'X' : '.',
+ ocr & SD_OCR_VDD_29_30 ? 'X' : '.',
+ ocr & SD_OCR_VDD_30_31 ? 'X' : '.',
+ ocr & SD_OCR_VDD_31_32 ? 'X' : '.',
+ ocr & SD_OCR_VDD_32_33 ? 'X' : '.',
+ ocr & SD_OCR_VDD_30_31 ? 'X' : '.',
+ ocr & SD_OCR_VDD_31_32 ? 'X' : '.',
+ ocr & SD_OCR_VDD_32_33 ? 'X' : '.',
+ ocr & SD_OCR_VDD_33_34 ? 'X' : '.',
+ ocr & SD_OCR_VDD_34_35 ? 'X' : '.',
+ ocr & SD_OCR_VDD_35_36 ? 'X' : '.');
+#endif
+ /* Verify that arg[23:15] range fits within OCR[23:15] range */
+ if ((ocr & vdd_range) != vdd_range)
+ rc = SDMMC_NOT_SUPPORTED;
+ if (*low_sig_lvl)
+ *low_sig_lvl = ocr & SD_OCR_S18A ? true : false;
+ *hc = ocr & SD_OCR_CCS ? true : false;
+ }
+ return rc;
+
+}
+
+
+/**
+ * From the selected card get its SD CARD Configuration Register (SCR).
+ * ACMD51 is valid under the Transfer state.
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pSCR Pointer to an 8-byte buffer receiving the contents of the SCR.
+ * The buffer shall follow the peripheral and DMA alignment requirements.
+ * \param pResp Pointer to where the response is returned.
+ * \return The command transfer result (see SendCommand).
+ */
+uint8_t Acmd51(SdmmcDriver *drv, uint8_t * pSCR, uint32_t * pResp)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t error;
+
+
+ TRACE_1("Acmd%u\n\r", 51);
+
+ error = Cmd55(drv, drv->card.wAddress);
+
+ if (!error) {
+
+ _ResetCmd(pCmd);
+
+ pCmd->bCmd = 51;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
+
+ if (pSCR) {
+ pCmd->wBlockSize = 64 / 8;
+ pCmd->wNbBlocks = 1;
+ pCmd->pData = pSCR;
+ }
+ pCmd->pResp = pResp;
+ drv->timeout_elapsed = -1;
+ error = sdmmcSendCmd(drv);
+
+ } else {
+ if (error) {
+ TRACE_2("Acmd%u %s\n\r", 51, SD_StringifyRetCode(error));
+ }
+ }
+ return error;
+}
+
+/**
+ * SEND_EXT_CSD, to get EXT_CSD register as a block of data.
+ * Valid under "trans" state.
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pEXT 512 byte buffer pointer for EXT_CSD data.
+ * The buffer shall follow the peripheral and DMA alignment requirements.
+ * \return 0 if successful;
+ * otherwise returns SD_ERROR_NORESPONSE if the card did not answer
+ * the command, or SDMMC_ERROR.
+ */
+#ifndef SDMMC_TRIM_MMC
+
+/**
+ * Switches the mode of operation of the selected card or
+ * Modifies the EXT_CSD registers.
+ * CMD6 is valid under the "trans" state.
+ * \return The command transfer result (see SendMciCommand).
+ * \param pSd Pointer to a SD/MMC card driver instance.
+ * \param pSwitchArg Pointer to a MmcCmd6Arg instance.
+ * \param pResp Pointer to where the response is returned.
+ */
+uint8_t MmcCmd6(SdmmcDriver *drv, const void *pSwitchArg, uint32_t * pResp)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t bRc;
+ MmcCmd6Arg *pMmcSwitch;
+
+ //assert(pSd);
+// assert(pSwitchArg);
+
+ _ResetCmd(&drv->cmd);
+
+ pMmcSwitch = (MmcCmd6Arg *) pSwitchArg;
+ pCmd->bCmd = 6;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(1) | SDMMC_CMD_bmBUSY;
+ pCmd->dwArg = (pMmcSwitch->access << 24)
+ | (pMmcSwitch->index << 16)
+ | (pMmcSwitch->value << 8)
+ | (pMmcSwitch->cmdSet << 0);
+ pCmd->pResp = pResp;
+ drv->timeout_elapsed = -1;
+ bRc = sdmmcSendCmd(drv);
+
+ if (!bRc && pResp && *pResp & STATUS_MMC_SWITCH) {
+ TRACE_1("st %lx\n\r", *pResp);
+ }
+ return bRc;
+}
+
+uint8_t MmcCmd8(SdmmcDriver *drv)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ pCmd->bCmd = 8;
+ pCmd->cmdOp.wVal = SDMMC_CMD_CDATARX(1);
+ pCmd->wBlockSize = 512;
+ pCmd->wNbBlocks = 1;
+ pCmd->pData = drv->card.EXT;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+
+ return bRc;
+}
+#endif
+
+/**
+ * SDIO SEND OPERATION CONDITION (OCR) command.
+ * Sends host capacity support information and acrivates the card's
+ * initialization process.
+ * \return The command transfer result (see SendMciCommand).
+ * \param drv Pointer to \ref SdmmcDriver instance.
+ * \param pIo Pointer to data sent as well as response buffer (32bit).
+ */
+#ifndef SDMMC_TRIM_SDIO
+uint8_t Cmd5(SdmmcDriver *drv, uint32_t * pIo)
+{
+ sSdmmcCommand *pCmd = &drv->cmd;
+ uint8_t bRc;
+
+ _ResetCmd(&drv->cmd);
+
+ /* Fill command */
+ pCmd->cmdOp.wVal = SDMMC_CMD_CNODATA(4) | SDMMC_CMD_bmIO
+ | SDMMC_CMD_bmOD;
+ pCmd->bCmd = 5;
+ pCmd->dwArg = *pIo;
+ pCmd->pResp = pIo;
+ drv->timeout_elapsed = -1;
+ /* Send command */
+ bRc = sdmmcSendCmd(drv);
+ return bRc;
+}
+#endif
+
+
+uint8_t CancelCommand(SdmmcDriver *driver)
+{
+ //assert(set);
+ osalDbgCheck(driver->state != MCID_OFF);
+
+ Sdmmc *regs = driver->regs;
+ sSdmmcCommand *cmd = &driver->cmd;
+ uint32_t response; /* The R1 response is 32-bit long */
+ uint32_t usec, rc;
+
+ cmd->pResp = &response;
+ cmd->cmdOp.wVal = SDMMC_CMD_CSTOP | SDMMC_CMD_bmBUSY;
+ cmd->bCmd = 12;
+
+ if (driver->state != MCID_CMD && driver->state != MCID_ERROR)
+ return SDMMC_STATE;
+ //trace_debug("Requested to cancel CMD%u\n\r", set->cmd ? set->cmd->bCmd : 99);
+ if (driver->state == MCID_ERROR) {
+ driver->state = MCID_LOCKED;
+ return SDMMC_OK;
+ }
+ //assert(cmd);
+ /* Asynchronous Abort, if a data transfer has been started */
+ if (cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_TX
+ || cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_RX) {
+ /* May the CMD line still be busy, reset it */
+ if (regs->SDMMC_PSR & SDMMC_PSR_CMDINHC) {
+ regs->SDMMC_SRR |= SDMMC_SRR_SWRSTCMD;
+ while (regs->SDMMC_SRR & SDMMC_SRR_SWRSTCMD) ;
+ }
+ /* Issue the STOP_TRANSMISSION command. */
+ driver->state = MCID_LOCKED;
+ driver->resp_len = 0;
+ driver->blk_index = 0;
+ driver->cmd_line_released = false;
+ driver->dat_lines_released = false;
+ driver->expect_auto_end = false;
+
+ rc = sdmmc_device_command(driver);
+
+ if (rc == SDMMC_OK) {
+ for (usec = 0; driver->state == MCID_CMD && usec < 500000; usec+= 10) {
+
+ t_usleep(driver,10);
+
+ if (driver->use_polling) {
+ sdmmc_device_poll(driver);
+ }
+ }
+ }
+ }
+ /* Reset CMD and DATn lines */
+ regs->SDMMC_SRR |= SDMMC_SRR_SWRSTDAT | SDMMC_SRR_SWRSTCMD;
+ while (regs->SDMMC_SRR & (SDMMC_SRR_SWRSTDAT | SDMMC_SRR_SWRSTCMD)) ;
+
+ /* Release command */
+ cmd->bStatus = SDMMC_USER_CANCEL;
+
+ driver->state = MCID_LOCKED;
+
+ driver->resp_len = 0;
+ driver->blk_index = 0;
+ driver->cmd_line_released = false;
+ driver->dat_lines_released = false;
+ driver->expect_auto_end = false;
+
+ return SDMMC_OK;
+}
+
+
+uint8_t tuneSampling(SdmmcDriver *driver)
+{
+ //osalDbgCheck(set);
+ osalDbgCheck(driver->state != MCID_OFF && driver->state != MCID_CMD);
+
+ Sdmmc *regs = driver->regs;
+ uint32_t response; /* The R1 response is 32-bit long */
+
+ //test command
+ driver->cmd.pData = (uint8_t *)&response;
+ driver->cmd.wBlockSize = 128;
+ driver->cmd.wNbBlocks = 1;
+ driver->cmd.pResp = &response;
+ driver->cmd.dwArg = 0;
+ driver->cmd.cmdOp.wVal = SDMMC_CMD_CDATARX(1);
+ driver->cmd.bCmd = 21;
+ uint16_t hc2r;
+ uint8_t rc = SDMMC_OK, ix;
+
+ if (driver->tim_mode != SDMMC_TIM_MMC_HS200)
+ driver->cmd.bCmd = 19;
+ ix = sdmmc_get_bus_width(driver);
+ if (ix == 4)
+ driver->cmd.wBlockSize = 64;
+ else if (ix != 8)
+ return SDMMC_PARAM;
+ /* Start the tuning procedure */
+ regs->SDMMC_HC2R |= SDMMC_HC2R_EXTUN;
+ hc2r = regs->SDMMC_HC2R;
+ for (ix = 0; hc2r & SDMMC_HC2R_EXTUN && ix < 40; ix++) {
+ /* Issue the SEND_TUNING_BLOCK command */
+ driver->state = MCID_LOCKED;
+ driver->resp_len = 0;
+ driver->blk_index = 0;
+ driver->cmd_line_released = false;
+ driver->dat_lines_released = false;
+ driver->expect_auto_end = false;
+ rc = sdmmc_device_command(driver);
+
+ if (rc != SDMMC_OK)
+ break;
+
+ /* While tuning the position of the sampling point, usual
+ * interrupts do not occur. Expect NISTR:BRDRDY only. */
+ while (!(regs->SDMMC_NISTR & SDMMC_NISTR_BRDRDY)) ;
+ regs->SDMMC_NISTR = SDMMC_NISTR_BRDRDY;
+ //driver->cmd = NULL;
+ hc2r = regs->SDMMC_HC2R;
+ }
+ if (hc2r & SDMMC_HC2R_EXTUN) {
+ /* Abort the tuning procedure */
+ regs->SDMMC_HC2R = hc2r & ~SDMMC_HC2R_EXTUN;
+ /* Reset the tuning circuit. Use the fixed clock when sampling
+ * data. */
+ regs->SDMMC_HC2R = hc2r & ~SDMMC_HC2R_SCLKSEL
+ & ~SDMMC_HC2R_EXTUN;
+ rc = SDMMC_ERR;
+ }
+ else if (!(hc2r & SDMMC_HC2R_SCLKSEL))
+ rc = SDMMC_ERR;
+ /* Clear residual interrupts, if any */
+ if (regs->SDMMC_NISTR & SDMMC_NISTR_ERRINT)
+ regs->SDMMC_EISTR = regs->SDMMC_EISTR;
+ regs->SDMMC_NISTR = regs->SDMMC_NISTR;
+ driver->state = MCID_LOCKED;
+ driver->resp_len = 0;
+ driver->blk_index = 0;
+ driver->cmd_line_released = false;
+ driver->dat_lines_released = false;
+ driver->expect_auto_end = false;
+ //trace_debug("%u tuning blocks. %s.\n\r", ix, SD_StringifyRetCode(rc));
+ return rc;
+}
+
+static void _ResetCmd(sSdmmcCommand * pCmd)
+{
+ memset(pCmd, 0, sizeof (sSdmmcCommand));
+}
+
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.h
new file mode 100644
index 000000000..014986e22
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_cmds.h
@@ -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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.c
new file mode 100644
index 000000000..ca28cc051
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.c
@@ -0,0 +1,1994 @@
+#include <string.h>
+#include "hal.h"
+#include "sama_sdmmc_lld.h"
+#include "ch_sdmmc_device.h"
+#include "ch_sdmmc_cmds.h"
+#include "ch_sdmmc_pmc.h"
+#include "ch_sdmmc_tc.h"
+#include "ch_sdmmc_sdio.h"
+#include "ch_sdmmc_sd.h"
+#include "ch_sdmmc_mmc.h"
+/** A software event, never raised by the hardware, specific to this driver */
+#define SDMMC_NISTR_CUSTOM_EVT (0x1u << 13)
+
+/** Device status */
+#define STAT_ADDRESS_OUT_OF_RANGE (1UL << 31)
+#define STAT_ADDRESS_MISALIGN (1UL << 30)
+#define STAT_BLOCK_LEN_ERROR (1UL << 29)
+#define STAT_ERASE_SEQ_ERROR (1UL << 28)
+#define STAT_ERASE_PARAM (1UL << 27)
+#define STAT_WP_VIOLATION (1UL << 26)
+#define STAT_DEVICE_IS_LOCKED (1UL << 25)
+#define STAT_LOCK_UNLOCK_FAILED (1UL << 24)
+#define STAT_COM_CRC_ERROR (1UL << 23)
+#define STAT_ILLEGAL_COMMAND (1UL << 22)
+#define STAT_DEVICE_ECC_FAILED (1UL << 21)
+#define STAT_CC_ERROR (1UL << 20)
+#define STAT_ERROR (1UL << 19)
+#define STAT_CID_OVERWRITE (1UL << 16)
+#define STAT_ERASE_SKIP (1UL << 15)
+#define STAT_CARD_ECC_DISABLED (1UL << 14)
+#define STAT_ERASE_RESET (1UL << 13)
+#define STAT_CURRENT_STATE (0xfUL << 9)
+#define STAT_READY_FOR_DATA (1UL << 8)
+#define STAT_SWITCH_ERROR (1UL << 7)
+#define STAT_EXCEPTION_EVENT (1UL << 6)
+#define STAT_APP_CMD (1UL << 5)
+
+union uint32_u {
+ uint32_t word;
+ uint8_t bytes[4];
+};
+
+static void calibrate_zout(Sdmmc * regs);
+void reset_peripheral(SdmmcDriver *driver);
+void sdmmc_set_capabilities(
+ Sdmmc * regs,
+ uint32_t caps0, uint32_t caps0_mask,
+ uint32_t caps1, uint32_t caps1_mask);
+
+static uint8_t HwReset(SdmmcDriver *driver);
+
+
+
+static void sdmmc_get_response(SdmmcDriver *driver, sSdmmcCommand *cmd, bool complete, uint32_t *out);
+static bool sdmmc_is_busy(SdmmcDriver *driver);
+static uint8_t sdmmc_build_dma_table( SdmmcDriver *driver );
+static uint8_t unplug_device(SdmmcDriver *driver);
+static uint8_t sdmmc_set_speed_mode(SdmmcDriver *driver, uint8_t mode,bool verify);
+static uint8_t sdmmc_set_bus_width(SdmmcDriver *driver, uint8_t bits);
+
+
+uint8_t sdmmc_device_lowlevelcfg(SdmmcDriver *driver)
+{
+ uint8_t res;
+
+
+ pmc_set_main_oscillator_freq(BOARD_MAIN_CLOCK_EXT_OSC);
+
+ TRACE_1("Processor clock: %u MHz\r\n", ((unsigned)(pmc_get_processor_clock() / 1000000) ));
+ TRACE_1("Master clock: %u MHz\r\n", ((unsigned)(pmc_get_master_clock() / 1000000)) );
+
+
+ driver->tctimer_id = get_tc_id_from_addr(driver->config->tctimer,driver->config->tc_chan);
+ pmc_configure_peripheral(driver->tctimer_id, NULL, true);
+
+
+
+ if (driver->config->slot_id == SDMMC_SLOT0) {
+ driver->regs = SDMMC0;
+ pmcEnableSDMMC0()
+ ;
+ } else if (driver->config->slot_id == SDMMC_SLOT1) {
+ driver->regs = SDMMC1;
+ pmcEnableSDMMC1()
+ ;
+ }
+
+ pmc_configure_peripheral((SDMMC_SLOT0 + driver->config->slot_id), NULL, true);
+
+ if (driver->config->use_fastest_clock) {
+
+ pmc_enable_upll_clock();
+ pmc_enable_upll_bias();
+ }
+
+ pmc_configure_peripheral((SDMMC_SLOT0 + driver->config->slot_id),
+ &driver->config->pmccfg, true);
+
+
+ switch (driver->config->slot_id) {
+
+ case SDMMC_SLOT0: {
+
+ uint32_t caps0 = BOARD_SDMMC0_CAPS0;
+
+ /* Program capabilities for SDMMC0 */
+ sdmmc_set_capabilities((Sdmmc*) SDMMC0, caps0, CAPS0_MASK, 0, 0);
+
+ /* Configure SDMMC0 pins */
+
+ /** SDMMC0 pin Card Detect (CD) */
+ //#define PIN_SDMMC0_CD_IOS1 { PIO_GROUP_A, PIO_PA13A_SDMMC0_CD, PIO_PERIPH_A, PIO_PULLUP }
+ palSetGroupMode(PIOA, PIO_PA13A_SDMMC0_CD, 0U,
+ PAL_SAMA_FUNC_PERIPH_A | PAL_MODE_INPUT_PULLUP);
+
+ /** SDMMC0 pin Card Clock (CK) */
+ //#define PIN_SDMMC0_CK_IOS1 { PIO_GROUP_A, PIO_PA0A_SDMMC0_CK, PIO_PERIPH_A, PIO_DEFAULT }
+ palSetGroupMode(PIOA, PIO_PA0A_SDMMC0_CK, 0U, PAL_SAMA_FUNC_PERIPH_A);
+
+ /** SDMMC0 pin Card Command (CMD) */
+ //#define PIN_SDMMC0_CMD_IOS1 { PIO_GROUP_A, PIO_PA1A_SDMMC0_CMD, PIO_PERIPH_A, PIO_PULLUP }
+ palSetGroupMode(PIOA, PIO_PA1A_SDMMC0_CMD, 0U,
+ PAL_SAMA_FUNC_PERIPH_A | PAL_MODE_INPUT_PULLUP);
+
+ /** SDMMC0 pin Card Reset (RSTN) */
+ //#define PIN_SDMMC0_RSTN_IOS1 { PIO_GROUP_A, PIO_PA10A_SDMMC0_RSTN, PIO_PERIPH_A, PIO_PULLUP }
+ palSetGroupMode(PIOA, PIO_PA10A_SDMMC0_RSTN, 0U,
+ PAL_SAMA_FUNC_PERIPH_A | PAL_MODE_INPUT_PULLUP);
+
+ /** SDMMC0 pin VDD Selection (VDDSEL) */
+ //#define PIN_SDMMC0_VDDSEL_IOS1 { PIO_GROUP_A, PIO_PA11A_SDMMC0_VDDSEL, PIO_PERIPH_A, PIO_DEFAULT }
+ palSetGroupMode(PIOA, PIO_PA11A_SDMMC0_VDDSEL, 0U,
+ PAL_SAMA_FUNC_PERIPH_A);
+
+ /** SDMMC0 pin 8-bit Data (DA0-7) */
+ //#define PINS_SDMMC0_DATA8B_IOS1 { PIO_GROUP_A, 0x000003fc, PIO_PERIPH_A, PIO_PULLUP }
+ palSetGroupMode(PIOA, 0x000003fc, 0U,
+ PAL_SAMA_FUNC_PERIPH_A | PAL_MODE_INPUT_PULLUP);
+
+ res = 1;
+
+ }
+ break;
+ case SDMMC_SLOT1: {
+
+ uint32_t caps0 = BOARD_SDMMC1_CAPS0;
+
+ /* Program capabilities for SDMMC1 */
+ sdmmc_set_capabilities(SDMMC1, caps0, CAPS0_MASK, 0, 0);
+
+ /* Configure SDMMC1 pins */
+
+ /** SDMMC1 pin Card Detect (CD) */
+ palSetGroupMode(PIOA, PIO_PA30E_SDMMC1_CD, 0U,
+ PAL_SAMA_FUNC_PERIPH_E | PAL_MODE_INPUT_PULLUP);
+
+ /** SDMMC1 pin Card Clock (CK) */
+ // #define PIN_SDMMC1_CK_IOS1 { PIO_GROUP_A, PIO_PA22E_SDMMC1_CK, PIO_PERIPH_E, PIO_DEFAULT }
+ palSetGroupMode(PIOA, PIO_PA22E_SDMMC1_CK, 0U, PAL_SAMA_FUNC_PERIPH_E);
+
+ /** SDMMC1 pin Card Command (CMD) */
+ //#define PIN_SDMMC1_CMD_IOS1 { PIO_GROUP_A, PIO_PA28E_SDMMC1_CMD, PIO_PERIPH_E, PIO_PULLUP }
+ palSetGroupMode(PIOA, PIO_PA28E_SDMMC1_CMD, 0U,
+ PAL_SAMA_FUNC_PERIPH_E | PAL_MODE_INPUT_PULLUP);
+
+ /** SDMMC1 pin 4-bit Data (DA0-3) */
+ //#define PINS_SDMMC1_DATA4B_IOS1 { PIO_GROUP_A, 0x003c0000, PIO_PERIPH_E, PIO_PULLUP }
+ palSetGroupMode(PIOA, 0x003c0000, 0U,
+ PAL_SAMA_FUNC_PERIPH_E | PAL_MODE_INPUT_PULLUP);
+
+ res = 1;
+ }
+ break;
+ default:
+ res = 0;
+ break;
+ }
+
+
+ if (res) {
+ //check res
+ res = IS_CACHE_ALIGNED(driver->config->data_buf);
+ TRACE_2("check data buf %d %08x\r\n", res, driver->config->data_buf);
+ res &= IS_CACHE_ALIGNED(driver->config->data_buf_size);
+ TRACE_2("check data_buf_size %d %08x\r\n", res,
+ driver->config->data_buf_size);
+ res &= IS_CACHE_ALIGNED(driver->card.EXT);
+ TRACE_2("check libExt %d %08x\r\n", res, driver->card.EXT);
+ //res &= IS_CACHE_ALIGNED(sizeof(driver->card.EXT));
+ //TRACE_2("check size libExt %d %08x\r\n",rc,sizeof(driver->card.EXT));
+
+ if (!res) {
+ TRACE(
+ "WARNING: buffers are not aligned on data cache lines. Please fix this before enabling DMA.\n\r");
+ driver->use_polling = true;
+ } else {
+ driver->use_polling = false;
+ }
+
+ }
+
+ return res;
+
+}
+
+bool sdmmc_device_initialize(SdmmcDriver *driver)
+{
+
+ uint32_t base_freq, power, val;
+ const uint8_t max_exp = (SDMMC_TCR_DTCVAL_Msk >> SDMMC_TCR_DTCVAL_Pos) - 1;
+ uint8_t exp;
+
+ driver->use_set_blk_cnt = false;
+
+ val = (driver->regs->SDMMC_CA0R & SDMMC_CA0R_MAXBLKL_Msk) >> SDMMC_CA0R_MAXBLKL_Pos;
+
+ driver->blk_size = (val <= 0x2 ? (512 << val) : 512);
+
+ //Configure the TC Timer
+
+ pmc_configure_peripheral(driver->tctimer_id, NULL, true);
+
+ tc_configure(driver->config->tctimer, driver->config->tc_chan, TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_CPCDIS | TC_CMR_BURST_NONE | TC_CMR_TCCLKS_TIMER_CLOCK2);
+
+ driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_EMR |= TC_EMR_NODIVCLK;
+
+ /* Perform the initial I/O calibration sequence, manually.
+ * Allow tSTARTUP = 2 usec for the analog circuitry to start up.
+ * CNTVAL = fHCLOCK / (4 * (1 / tSTARTUP)) */
+ val = pmc_get_peripheral_clock(ID_SDMMC0+driver->config->slot_id);
+ val = ROUND_INT_DIV(val, 4 * 500000UL);
+
+
+ osalDbgCheck( (!(val << SDMMC_CALCR_CNTVAL_Pos & ~SDMMC_CALCR_CNTVAL_Msk)) );
+
+ driver->regs->SDMMC_CALCR = (driver->regs->SDMMC_CALCR & ~SDMMC_CALCR_CNTVAL_Msk & ~SDMMC_CALCR_TUNDIS) | SDMMC_CALCR_CNTVAL(val);
+
+ calibrate_zout(driver->regs);
+
+ /* Set DAT line timeout error to occur after 500 ms waiting delay.
+ * 500 ms is the timeout value to implement when writing to SDXC cards.
+ */
+ base_freq = (driver->regs->SDMMC_CA0R & SDMMC_CA0R_TEOCLKF_Msk) >> SDMMC_CA0R_TEOCLKF_Pos;
+ base_freq *= driver->regs->SDMMC_CA0R & SDMMC_CA0R_TEOCLKU ? 1000000UL : 1000UL;
+ /* 2 ^ (DTCVAL + 13) = TIMEOUT * FTEOCLK = FTEOCLK / 2 */
+ val = base_freq / 2;
+ for (exp = 31, power = 1UL << 31; !(val & power) && power != 0;
+ exp--, power >>= 1) ;
+ if (power == 0) {
+ //trace_warning("FTEOCLK is unknown\n\r");
+ exp = max_exp;
+ }
+ else {
+ exp = exp + 1 - 13;
+ exp = (uint8_t)min_u32(exp, max_exp);
+ }
+
+ driver->regs->SDMMC_TCR = (driver->regs->SDMMC_TCR & ~SDMMC_TCR_DTCVAL_Msk) | SDMMC_TCR_DTCVAL(exp);
+
+ TRACE_1("Set DAT line timeout to %lu ms\n\r", (10UL << (exp + 13UL))/ (base_freq / 100UL));
+
+ /* Reset the peripheral. This will reset almost all registers.
+ * It doesn't affect I/O calibration however. */
+ reset_peripheral(driver);
+ /* As sdmmc_reset_peripheral deliberately preserves MC1R.FCD, this field
+ * has yet to be initialized. As the controller may disable outputs
+ * depending on the state of the card detection input, this input should
+ * be neutralized when the device is embedded. */
+ if ( (driver->regs->SDMMC_CA0R & SDMMC_CA0R_SLTYPE_Msk) == SDMMC_CA0R_SLTYPE_EMBEDDED)
+ driver->regs->SDMMC_MC1R |= SDMMC_MC1R_FCD;
+ else
+ driver->regs->SDMMC_MC1R &= ~SDMMC_MC1R_FCD;
+
+
+
+ return true;
+}
+/**
+ * Run the SDcard initialization sequence. This function runs the
+ * initialisation procedure and the identification process, then it sets the
+ * SD card in transfer state to set the block length and the bus width.
+ * \return 0 if successful; otherwise returns an \ref sdmmc_rc "error code".
+ * \param pSd Pointer to a SD card driver instance.
+ */
+uint8_t sdmmc_device_start(SdmmcDriver *drv)
+{
+ uint32_t freq;
+ uint8_t error;
+
+ SdParamReset(&drv->card);
+
+ /* Power the device and the bus on */
+ HwPowerDevice(drv, SDMMC_PWR_STD);
+ /* Reset the controller to default timing mode and data bus width */
+ HwSetHsMode(drv, SDMMC_TIM_MMC_BC);
+
+ HwSetBusWidth(drv, 1);
+ /* For device identification, clock the device at fOD */
+ freq = 400000ul;
+
+ error = HwSetClock(drv, &freq);
+
+ if (error != SDMMC_OK && error != SDMMC_CHANGED) {
+ return error;
+ }
+
+ //if (SD_GetStatus(drv) == SDMMC_NOT_SUPPORTED) {
+ // TRACE("Device not detected.\n\r");
+ // return SDMMC_NOT_SUPPORTED;
+ // }
+
+ /* Initialization delay: The maximum of 1 msec, 74 clock cycles and supply
+ * ramp up time. Supply ramp up time provides the time that the power is
+ * built up to the operating level (the bus master supply voltage) and the
+ * time to wait until the SD card can accept the first command. */
+ /* Power On Init Special Command */
+ error = CmdPowerOn(drv);
+
+ t_msleep(drv,200);
+
+ if (error) {
+ return error;
+ }
+
+
+ return SDMMC_OK;
+}
+
+uint8_t sdmmc_device_identify(SdmmcDriver *drv)
+{
+ uint8_t error;
+ bool retry = false;
+
+ if (drv->state != MCID_IDLE )
+ return SDMMC_STATE;
+
+ do {
+ /* After power-on or CMD0, all cards?
+ * CMD lines are in input mode, waiting for start bit of the next command.
+ * The cards are initialized with a default relative card address
+ * (RCA=0x0000) and with a default driver stage register setting
+ * (lowest speed, highest driving current capability). */
+ error = SdMmcIdentify(drv);
+
+ if (error) {
+ TRACE_1("Identify %s\n\r", SD_StringifyRetCode(error));
+ return error;
+ }
+
+ if ((drv->card.bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmSD) {
+
+ error = SDMMC_Lib_SdStart(drv, &retry);
+ /* Handle the case where the both the slot and the device are
+ * UHS-I-capable, but the system doesn't support powering the
+ * card off, when SD_DeInit is called. As a result, from the
+ * device's perspective, the voltage switch sequence has been
+ * taken already. */
+ if (error && retry) {
+ HwPowerDevice(drv, SDMMC_PWR_STD_VDD_LOW_IO);
+
+ drv->card.bCardSigLevel = 0;
+
+ error = HwSetHsMode(drv, SDMMC_TIM_SD_SDR12);
+
+ HwSetBusWidth(drv, 1);
+
+ if (!error) {
+ drv->card.bSpeedMode = SDMMC_TIM_SD_SDR12;
+ //goto Retry;
+ }
+ }
+ }
+
+
+ #ifndef SDMMC_TRIM_SDIO
+ else if (drv->card.bCardType & CARD_TYPE_bmSDIO)
+ error = SdioInit(drv);
+ #endif
+ #ifndef SDMMC_TRIM_MMC
+ else if ((drv->card.bCardType & CARD_TYPE_bmSDMMC) == CARD_TYPE_bmMMC)
+ error = MmcInit(drv);
+ #endif
+ else {
+ TRACE_1("Identify %s\n\r", "failed");
+ return SDMMC_NOT_INITIALIZED;
+ }
+ if (error) {
+ TRACE_1("Init %s\n\r", SD_StringifyRetCode(error));
+ return error;
+ }
+
+ } while (retry==1);
+
+ drv->card.bStatus = SDMMC_OK;
+
+ return SDMMC_OK;
+}
+
+void sdmmc_device_deInit(SdmmcDriver *drv)
+{
+ HwReset(drv);
+ SdParamReset(&drv->card);
+
+ memset(&drv->cmd, 0, sizeof(drv->cmd));
+}
+
+
+/**
+ * \brief Fetch events from the SDMMC peripheral, handle them, and proceed to
+ * the subsequent step, w.r.t. the SD/MMC command being processed.
+ * \warning This implementation suits LITTLE ENDIAN hosts only.
+ */
+ void sdmmc_device_poll(SdmmcDriver *driver)
+
+ {
+ osalDbgCheck(driver->state != MCID_OFF);
+
+ Sdmmc *regs = driver->regs;
+ sSdmmcCommand *cmd = &driver->cmd;
+ uint16_t events, errors, acesr;
+ bool has_data;
+
+ if (driver->state != MCID_CMD)
+ return;
+ //osalDbgCheck(cmd);
+ has_data = (cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_TX) || (cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_RX);
+
+ Fetch:
+ /* Fetch normal events */
+ events = regs->SDMMC_NISTR;
+ if (driver->use_polling) {
+
+ if (driver->expect_auto_end
+
+ && !(driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_SR & TC_SR_CLKSTA)
+
+ )
+ events |= SDMMC_NISTR_CUSTOM_EVT;
+ } else {
+ if (driver->expect_auto_end) {
+ while (driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_SR & TC_SR_CLKSTA);
+
+ events |= SDMMC_NISTR_CUSTOM_EVT;
+ }
+ }
+ if (!events)
+ return;
+ //TRACE_1("events %08x\n\r",events);
+ /* Check the global error flag */
+ if (events & SDMMC_NISTR_ERRINT) {
+ errors = regs->SDMMC_EISTR;
+ events &= ~SDMMC_NISTR_ERRINT;
+ /* Clear error interrupts */
+ regs->SDMMC_EISTR = errors;
+ if (errors & SDMMC_EISTR_CURLIM)
+ cmd->bStatus = SDMMC_NOT_INITIALIZED;
+ else if (errors & SDMMC_EISTR_CMDCRC)
+ cmd->bStatus = SDMMC_ERR_IO;
+ else if (errors & SDMMC_EISTR_CMDTEO)
+ cmd->bStatus = SDMMC_NO_RESPONSE;
+ else if (errors & (SDMMC_EISTR_CMDEND | SDMMC_EISTR_CMDIDX))
+ cmd->bStatus = SDMMC_ERR_IO;
+ else if (errors & SDMMC_EISTR_TUNING)
+ cmd->bStatus = SDMMC_ERR_IO;
+ /* TODO upon SDMMC_EISTR_TUNING, clear HC2R:SCLKSEL, and perform
+ * the tuning procedure */
+ /* TODO if SDMMC_NISTR_TRFC and only SDMMC_EISTR_DATTEO then
+ * ignore SDMMC_EISTR_DATTEO */
+ else if (errors & SDMMC_EISTR_DATTEO)
+ cmd->bStatus = SDMMC_ERR_IO;
+ else if (errors & (SDMMC_EISTR_DATCRC | SDMMC_EISTR_DATEND))
+ cmd->bStatus = SDMMC_ERR_IO;
+ else if (errors & SDMMC_EISTR_ACMD) {
+ acesr = regs->SDMMC_ACESR;
+ if (acesr & SDMMC_ACESR_ACMD12NE)
+ cmd->bStatus = SDMMC_ERR;
+ else if (acesr & SDMMC_ACESR_ACMDCRC)
+ cmd->bStatus = SDMMC_ERR_IO;
+ else if (acesr & SDMMC_ACESR_ACMDTEO)
+ cmd->bStatus = SDMMC_NO_RESPONSE;
+ else if (acesr & (SDMMC_ACESR_ACMDEND | SDMMC_ACESR_ACMDIDX))
+ cmd->bStatus = SDMMC_ERR_IO;
+ else
+ cmd->bStatus = SDMMC_ERR;
+ }
+ else if (errors & SDMMC_EISTR_ADMA) {
+ //#if TRACE_LEVEL >= TRACE_LEVEL_ERROR
+ // const uint32_t desc_ix = (regs->SDMMC_ASA0R -
+ // (uint32_t)set->table) / (SDMMC_DMADL_SIZE * 4UL);
+ //
+ // trace_error("ADMA error 0x%x at desc. line[%lu]\n\r",
+ // regs->SDMMC_AESR, desc_ix);
+ //#endif
+ cmd->bStatus = SDMMC_PARAM;
+ }
+ else if (errors & SDMMC_EISTR_BOOTAE)
+ cmd->bStatus = SDMMC_STATE;
+ else
+ cmd->bStatus = SDMMC_ERR;
+ driver->state = cmd->bCmd == 12 ? MCID_LOCKED : MCID_ERROR;
+ //TRACE_3("CMD%u ended with error flags %04x, cmd status %s\n\r", cmd->bCmd, errors, SD_StringifyRetCode(cmd->bStatus));
+ goto End;
+ }
+
+ /* No error. Give priority to the low-latency event that signals the
+ * completion of the Auto CMD12 command, hence of the whole multiple-
+ * block data transfer. */
+ if (events & SDMMC_NISTR_CUSTOM_EVT) {
+ //#ifndef NDEBUG
+ // if (!(set->regs->SDMMC_PSR & SDMMC_PSR_CMDLL))
+ // trace_warning("Auto command still ongoing\n\r");
+ //#endif
+ if (cmd->pResp) {
+ //TRACE("getting resp\r\n");
+ sdmmc_get_response(driver, cmd, true, cmd->pResp);
+ }
+ goto Succeed;
+ }
+
+ /* First, expect completion of the command */
+ if (events & SDMMC_NISTR_CMDC) {
+ //#ifndef NDEBUG
+ // if (cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_TX
+ // && !set->table && set->blk_index != cmd->wNbBlocks
+ // && !(regs->SDMMC_PSR & SDMMC_PSR_WTACT))
+ // trace_warning("Write transfer not started\n\r");
+ // else if (cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_RX
+ // && !set->table && set->blk_index != cmd->wNbBlocks
+ // && !(regs->SDMMC_PSR & SDMMC_PSR_RTACT))
+ // trace_warning("Read transfer not started\n\r");
+ //#endif
+ /* Clear this normal interrupt */
+ regs->SDMMC_NISTR = SDMMC_NISTR_CMDC;
+ events &= ~SDMMC_NISTR_CMDC;
+ driver->cmd_line_released = true;
+ /* Retrieve command response */
+ if (cmd->pResp) {
+ //TRACE("getting resp..\r\n");
+ sdmmc_get_response(driver, cmd, driver->dat_lines_released,
+ cmd->pResp);
+ }
+ if ((!has_data && !cmd->cmdOp.bmBits.checkBsy)
+ || driver->dat_lines_released)
+ goto Succeed;
+ }
+
+ /* Expect the next incoming block of data */
+ if (events & SDMMC_NISTR_BRDRDY
+ && cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_RX && !driver->config->dma_table) {
+ /* FIXME may be optimized by looping while PSR.BUFRDEN == 1 */
+ uint8_t *in, *out, *bound;
+ union uint32_u val;
+ uint16_t count;
+
+ /* Clear this normal interrupt */
+ regs->SDMMC_NISTR = SDMMC_NISTR_BRDRDY;
+ events &= ~SDMMC_NISTR_BRDRDY;
+
+ if (driver->blk_index >= cmd->wNbBlocks) {
+ // trace_error("Excess of incoming data\n\r");
+ cmd->bStatus = SDMMC_ERR_IO;
+ driver->state = MCID_ERROR;
+ goto End;
+ }
+ out = cmd->pData + driver->blk_index * (uint32_t)cmd->wBlockSize;
+ count = cmd->wBlockSize & ~0x3;
+ for (bound = out + count; out < bound; out += 4) {
+ //#ifndef NDEBUG
+ // if (!(regs->SDMMC_PSR & SDMMC_PSR_BUFRDEN))
+ // trace_error("Unexpected Buffer Read Disable status\n\r");
+ //#endif
+ val.word = regs->SDMMC_BDPR;
+ out[0] = val.bytes[0];
+ out[1] = val.bytes[1];
+ out[2] = val.bytes[2];
+ out[3] = val.bytes[3];
+ }
+ if (count < cmd->wBlockSize) {
+ //#ifndef NDEBUG
+ // if (!(regs->SDMMC_PSR & SDMMC_PSR_BUFRDEN))
+ // trace_error("Unexpected Buffer Read Disable status\n\r");
+ //#endif
+ val.word = regs->SDMMC_BDPR;
+ count = cmd->wBlockSize - count;
+ for (in = val.bytes, bound = out + count;
+ out < bound; in++, out++)
+ *out = *in;
+ }
+ #if 0 && !defined(NDEBUG)
+ if (regs->SDMMC_PSR & SDMMC_PSR_BUFRDEN)
+ trace_warning("Renewed Buffer Read Enable status\n\r");
+ #endif
+ driver->blk_index++;
+ }
+
+ /* Expect the Buffer Data Port to be ready to accept the next
+ * outgoing block of data */
+ if (events & SDMMC_NISTR_BWRRDY
+ && cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_TX && !driver->config->dma_table
+ && driver->blk_index < cmd->wNbBlocks) {
+ /* FIXME may be optimized by looping while PSR.BUFWREN == 1 */
+ uint8_t *in, *out, *bound;
+ union uint32_u val;
+ uint16_t count;
+
+ /* Clear this normal interrupt */
+ regs->SDMMC_NISTR = SDMMC_NISTR_BWRRDY;
+ events &= ~SDMMC_NISTR_BWRRDY;
+
+ in = cmd->pData + driver->blk_index * (uint32_t)cmd->wBlockSize;
+ count = cmd->wBlockSize & ~0x3;
+ for (bound = in + count; in < bound; in += 4) {
+ val.bytes[0] = in[0];
+ val.bytes[1] = in[1];
+ val.bytes[2] = in[2];
+ val.bytes[3] = in[3];
+ //#ifndef NDEBUG
+ // if (!(regs->SDMMC_PSR & SDMMC_PSR_BUFWREN))
+ // trace_error("Unexpected Buffer Write Disable status\n\r");
+ //#endif
+ regs->SDMMC_BDPR = val.word;
+ }
+ if (count < cmd->wBlockSize) {
+ count = cmd->wBlockSize - count;
+ for (val.word = 0, out = val.bytes, bound = in + count;
+ in < bound; in++, out++)
+ *out = *in;
+ //#ifndef NDEBUG
+ // if (!(regs->SDMMC_PSR & SDMMC_PSR_BUFWREN))
+ // trace_error("Unexpected Buffer Write Disable status\n\r");
+ //#endif
+ regs->SDMMC_BDPR = val.word;
+ }
+ #if 0 && !defined(NDEBUG)
+ if (regs->SDMMC_PSR & SDMMC_PSR_BUFWREN)
+ trace_warning("Renewed Buffer Write Enable status\n\r");
+ #endif
+ driver->blk_index++;
+ }
+ //#ifndef NDEBUG
+ // else if (events & SDMMC_NISTR_BWRRDY
+ // && cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_TX && !set->table
+ // && set->blk_index >= cmd->wNbBlocks)
+ // trace_warning("Excess Buffer Write Ready status\n\r");
+ //#endif
+
+ /* Expect completion of either the data transfer or the busy state. */
+ if (events & SDMMC_NISTR_TRFC) {
+ /* Deviation from the SD Host Controller Specification:
+ * the Auto CMD12 command/response (when enabled) is still in
+ * progress. We are on our own to figure out when CMD12 will
+ * have completed.
+ * In the meantime:
+ * 1. errors affecting the CMD12 command - essentially
+ * SDMMC_EISTR_ACMD - have not been detected yet.
+ * 2. SDMMC_RR[3] is not yet valid.
+ * Our workaround here consists in generating a third event
+ * further to Transfer Complete, after a predefined amount of
+ * time, sufficient for CMD12 to complete.
+ * Refer to sdmmc_send_command(), which has prepared our Timer/
+ * Counter for this purpose. */
+ if (has_data && (cmd->bCmd == 18 || cmd->bCmd == 25)
+ && !driver->use_set_blk_cnt) {
+
+ driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
+
+ driver->expect_auto_end = true;
+ //#ifndef NDEBUG
+ // if (!set->cmd_line_released)
+ // trace_warning("Command still ongoing\n\r");
+ //#endif
+ }
+ //#ifndef NDEBUG
+ // if (regs->SDMMC_PSR & SDMMC_PSR_WTACT)
+ // trace_error("Write transfer still active\n\r");
+ // if (regs->SDMMC_PSR & SDMMC_PSR_RTACT)
+ // trace_error("Read transfer still active\n\r");
+ //#endif
+ /* Clear this normal interrupt */
+ regs->SDMMC_NISTR = SDMMC_NISTR_TRFC;
+ events &= ~SDMMC_NISTR_TRFC;
+ driver->dat_lines_released = true;
+ /* Deviation from the SD Host Controller Specification:
+ * there are cases, notably CMD7 with address and R1b, where the
+ * Transfer Complete interrupt precedes Command Complete. In
+ * such cases, the command/response is still in progress, we
+ * shall wait for Command Complete. */
+ if (driver->cmd_line_released && !driver->expect_auto_end && cmd->pResp) {
+ //TRACE("getting resp...\r\n");
+ sdmmc_get_response(driver, cmd, true, cmd->pResp);
+ }
+ if (has_data && !driver->config->dma_table
+ && driver->blk_index != cmd->wNbBlocks) {
+ //trace_error("Incomplete data transfer\n\r");
+ cmd->bStatus = SDMMC_ERR_IO;
+ driver->state = MCID_ERROR;
+ goto End;
+ }
+ if (driver->cmd_line_released && !driver->expect_auto_end)
+ goto Succeed;
+ }
+
+ //#ifndef NDEBUG
+ // if (events)
+ // trace_warning("Unhandled NISTR events: 0x%04x\n\r", events);
+ //#endif
+ if (events)
+ regs->SDMMC_NISTR = events;
+ goto Fetch;
+
+ Succeed:
+ driver->state = MCID_LOCKED;
+ End:
+ /* Clear residual normal interrupts, if any */
+ if (events)
+ regs->SDMMC_NISTR = events;
+ #if 0 && !defined(NDEBUG)
+ if (set->resp_len == 1)
+ trace_debug("CMD%u got response %08lx\n\r", cmd->bCmd,
+ cmd->pResp[0]);
+ else if (set->resp_len == 4)
+ trace_debug("CMD%u got response %08lx %08lx %08lx %08lx\n\r",
+ cmd->bCmd, cmd->pResp[0], cmd->pResp[1], cmd->pResp[2],
+ cmd->pResp[3]);
+ #endif
+ /* Upon error, recover by resetting the CMD and DAT lines */
+ if (cmd->bStatus != SDMMC_OK && cmd->bStatus != SDMMC_CHANGED) {
+ /* Resetting DAT lines also aborts the DMA transfer - if any -
+ * and resets the DMA circuit. */
+ regs->SDMMC_SRR |= SDMMC_SRR_SWRSTDAT | SDMMC_SRR_SWRSTCMD;
+ while (regs->SDMMC_SRR & (SDMMC_SRR_SWRSTDAT
+ | SDMMC_SRR_SWRSTCMD)) ;
+ } else if (cmd->bCmd == 0 || (cmd->bCmd == 6
+ && cmd->dwArg & 1ul << 31 && !cmd->cmdOp.bmBits.checkBsy)) {
+ /* Currently in the function switching period, wait for the
+ * delay preconfigured in sdmmc_send_command(). */
+
+ driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
+ while (driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_SR & TC_SR_CLKSTA) ;
+
+ }
+
+ /* Release this command */
+
+ driver->resp_len = 0;
+ driver->blk_index = 0;
+ driver->cmd_line_released = false;
+ driver->dat_lines_released = false;
+ driver->expect_auto_end = false;
+
+ }
+
+ void sdmmc_set_device_clock(SdmmcDriver *driver, uint32_t freq)
+ {
+ osalDbgCheck(freq);
+
+ Sdmmc *regs = driver->regs;
+ uint32_t base_freq, div, low_freq, up_freq, new_freq;
+ uint32_t mult_freq, p_div, p_mode_freq;
+ uint16_t shval;
+ bool use_prog_mode = false;
+
+ freq = min_u32(freq, 120000000ul);
+ #ifndef NDEBUG
+ //if (!(regs->SDMMC_PCR & SDMMC_PCR_SDBPWR))
+ // trace_error("Bus is off\n\r");
+ //if (regs->SDMMC_HC2R & SDMMC_HC2R_PVALEN)
+ // trace_error("Preset values enabled though not implemented\n\r");
+ #endif
+ /* In the Divided Clock Mode scenario, compute the divider */
+ base_freq = (regs->SDMMC_CA0R & SDMMC_CA0R_BASECLKF_Msk) >> SDMMC_CA0R_BASECLKF_Pos;
+ base_freq *= 1000000UL;
+ /* DIV = FBASECLK / (2 * FSDCLK) */
+ div = base_freq / (2 * freq);
+ if (div >= 0x3ff)
+ div = 0x3ff;
+ else {
+ up_freq = base_freq / (div == 0 ? 1UL : 2 * div);
+ low_freq = base_freq / (2 * (div + 1UL));
+ if (up_freq > freq && (up_freq - freq) > (freq - low_freq))
+ div += 1;
+ }
+ new_freq = base_freq / (div == 0 ? 1UL : 2 * div);
+
+ /* Now, in the Programmable Clock Mode scenario, compute the divider.
+ * First, retrieve the frequency of the Generated Clock feeding this
+ * peripheral. */
+ /* TODO fix CLKMULT value in CA1R capability register: the default value
+ * is 32 whereas the real value is 40.5 */
+ mult_freq = (regs->SDMMC_CA1R & SDMMC_CA1R_CLKMULT_Msk) >> SDMMC_CA1R_CLKMULT_Pos;
+ if (mult_freq != 0)
+ #if 0
+ mult_freq = base_freq * (mult_freq + 1);
+ #else
+ mult_freq = pmc_get_gck_clock(ID_SDMMC0+driver->config->slot_id);
+ #endif
+ if (mult_freq != 0) {
+ /* DIV = FMULTCLK / FSDCLK - 1 */
+ p_div = CEIL_INT_DIV(mult_freq, freq);
+ if (p_div > 0x3ff)
+ p_div = 0x3ff;
+ else if (p_div != 0)
+ p_div = p_div - 1;
+ p_mode_freq = mult_freq / (p_div + 1);
+ if (ABS_DIFF(freq, p_mode_freq) < ABS_DIFF(freq, new_freq)) {
+ use_prog_mode = true;
+ div = p_div;
+ new_freq = p_mode_freq;
+ }
+ }
+
+ /* Stop the output clock, so we can change the frequency.
+ * Deviation from the SD Host Controller Specification: if the internal
+ * clock was temporarily disabled, the controller would then switch to
+ * an irrelevant clock frequency.
+ * This issue has been observed, notably, when trying to switch from 25
+ * to 50 MHz. Keep the SDMMC internal clock enabled. */
+ shval = regs->SDMMC_CCR & ~SDMMC_CCR_SDCLKEN;
+ regs->SDMMC_CCR = shval;
+ driver->dev_freq = new_freq;
+ /* Select the clock mode */
+ if (use_prog_mode)
+ shval |= SDMMC_CCR_CLKGSEL;
+ else
+ shval &= ~SDMMC_CCR_CLKGSEL;
+ /* Set the clock divider, and start the SDMMC internal clock - if it
+ * wasn't started yet. */
+ shval = (shval & ~SDMMC_CCR_USDCLKFSEL_Msk & ~SDMMC_CCR_SDCLKFSEL_Msk)
+ | SDMMC_CCR_USDCLKFSEL(div >> 8) | SDMMC_CCR_SDCLKFSEL(div & 0xff)
+ | SDMMC_CCR_INTCLKEN;
+ regs->SDMMC_CCR = shval;
+ while (!(regs->SDMMC_CCR & SDMMC_CCR_INTCLKS)) ;
+ /* Now start the output clock */
+ regs->SDMMC_CCR |= SDMMC_CCR_SDCLKEN;
+ }
+
+
+
+
+ /**
+ * Here is the fSdmmcSendCommand-type callback.
+ * SD/MMC command.
+ * \param _set Pointer to driver instance data (struct sdmmc_set).
+ * \param cmd Pointer to the command to be sent. Owned by the caller. Shall
+ * remain valid until the command is completed or stopped. For commands which
+ * transfer data, mind the peripheral and DMA alignment requirements that the
+ * external data buffer shall meet. Especially when DMA is used to read from the
+ * device, in which case the buffer shall be aligned on entire cache lines.
+ * \return Return code, from the eSDMMC_RC enumeration. If SDMMC_OK, the command
+ * has been issued and the caller should:
+ * 1. poll on sdmmc_is_busy(),
+ * 2. once finished, check the result of the command in cmd->bStatus.
+ * TODO in future when libsdmmc will set it: call sSdmmcCommand::fCallback.
+ */
+ uint32_t sdmmc_device_command(SdmmcDriver *driver)
+ {
+ osalDbgCheck(driver->cmd.bCmd <= 63);
+
+ Sdmmc *regs = driver->regs;
+
+ const bool stop_xfer = driver->cmd.cmdOp.bmBits.xfrData == SDMMC_CMD_STOPXFR;
+
+ const bool has_data = (driver->cmd.cmdOp.bmBits.xfrData == SDMMC_CMD_TX) || (driver->cmd.cmdOp.bmBits.xfrData == SDMMC_CMD_RX);
+
+ const bool use_dma = (bool) (driver->use_polling == false) &&
+ (driver->cmd.bCmd != 21 || driver->tim_mode >= SDMMC_TIM_SD_DS) &&
+ (driver->cmd.bCmd != 19 || driver->tim_mode < SDMMC_TIM_SD_DS);
+
+ const bool wait_switch = (driver->cmd.bCmd == 0) ||
+ (driver->cmd.bCmd == 6 && driver->cmd.dwArg & 1ul << 31 && !driver->cmd.cmdOp.bmBits.checkBsy);
+
+ const bool multiple_xfer = (driver->cmd.bCmd == 18 ) || ( driver->cmd.bCmd == 25 );
+
+ const bool blk_count_prefix = (driver->cmd.bCmd == 18 || driver->cmd.bCmd == 25) && driver->use_set_blk_cnt;
+
+ const bool stop_xfer_suffix = (driver->cmd.bCmd == 18 || driver->cmd.bCmd == 25) && !driver->use_set_blk_cnt;
+
+ uint32_t eister;
+ uint32_t mask;
+ uint32_t len;
+ uint32_t cycles;
+ uint16_t cr;
+ uint16_t tmr;
+
+ uint8_t mc1r;
+ uint8_t rc = SDMMC_OK;
+
+ //TRACE_1("[command] start %d\r\n",driver->cmd.bCmd);
+
+ if (driver->state == MCID_OFF)
+ return SDMMC_STATE;
+
+ if (driver->cmd.cmdOp.bmBits.powerON == driver->cmd.cmdOp.bmBits.sendCmd) {
+ //trace_error("Invalid command\n\r");
+ return SDMMC_PARAM;
+ }
+ if (stop_xfer && driver->cmd.bCmd != 12 && driver->cmd.bCmd != 52) {
+ //trace_error("Inconsistent abort command\n\r");
+ return SDMMC_PARAM;
+ }
+ if (driver->cmd.cmdOp.bmBits.powerON) {
+ /* Special call, no command to send this time */
+ /* Wait for 74 SD Clock cycles, as per SD Card specification.
+ * The e.MMC Electrical Standard specifies tRSCA >= 200 usec. */
+ if (driver->dev_freq == 0) {
+ //trace_error("Shall enable the device clock first\n\r");
+ return SDMMC_STATE;
+ }
+
+ return SDMMC_OK;
+ }
+
+ if (has_data && (driver->cmd.wNbBlocks == 0 || driver->cmd.wBlockSize == 0
+ || driver->cmd.pData == NULL)) {
+ //trace_error("Invalid data\n\r");
+ return SDMMC_PARAM;
+ }
+ if (has_data && driver->cmd.wBlockSize > driver->blk_size) {
+ //trace_error("%u-byte data block size not supported\n\r", driver->cmd.wBlockSize);
+ return SDMMC_PARAM;
+ }
+
+ if (has_data && use_dma) {
+ /* Using DMA. Prepare the descriptor table. */
+ rc = sdmmc_build_dma_table(driver);
+
+ if (rc != SDMMC_OK && rc != SDMMC_CHANGED)
+ return rc;
+
+ len = (uint32_t)driver->cmd.wNbBlocks * (uint32_t)driver->cmd.wBlockSize;
+
+ if (driver->cmd.cmdOp.bmBits.xfrData == SDMMC_CMD_TX) {
+ /* Ensure the outgoing data can be fetched directly from
+ * RAM */
+ cacheCleanRegion(driver->cmd.pData, len);
+ }
+ else if (driver->cmd.cmdOp.bmBits.xfrData == SDMMC_CMD_RX) {
+ /* Invalidate the corresponding data cache lines now, so
+ * this buffer is protected against a global cache clean
+ * operation, that concurrent code may trigger.
+ * Warning: until the command is reported as complete,
+ * no code should read from this buffer, nor from
+ * variables cached in the same lines. If such
+ * anticipated reading had to be supported, the data
+ * cache lines would need to be invalidated twice: both
+ * now and upon Transfer Complete. */
+ cacheInvalidateRegion(driver->cmd.pData, len);
+
+ }
+ }
+
+ ///if (multiple_xfer && !has_data)
+ // trace_warning("Inconsistent data\n\r");
+ if (sdmmc_is_busy(driver)) {
+ //trace_error("Concurrent command\n\r");
+ return SDMMC_BUSY;
+ }
+ driver->state = MCID_CMD;
+ driver->resp_len = 0;
+ driver->blk_index = 0;
+ driver->cmd_line_released = false;
+ driver->dat_lines_released = false;
+ driver->expect_auto_end = false;
+ driver->cmd.bStatus = rc;
+ //TRACE_1("command set status %d\r\n",driver->cmd.bStatus);
+
+ tmr = (regs->SDMMC_TMR & ~SDMMC_TMR_MSBSEL & ~SDMMC_TMR_DTDSEL
+ & ~SDMMC_TMR_ACMDEN_Msk & ~SDMMC_TMR_BCEN & ~SDMMC_TMR_DMAEN)
+ | SDMMC_TMR_ACMDEN_DIS;
+ mc1r = (regs->SDMMC_MC1R & ~SDMMC_MC1R_OPD & ~SDMMC_MC1R_CMDTYP_Msk)
+ | SDMMC_MC1R_CMDTYP_NORMAL;
+ cr = (regs->SDMMC_CR & ~SDMMC_CR_CMDIDX_Msk & ~SDMMC_CR_CMDTYP_Msk
+ & ~SDMMC_CR_DPSEL & ~SDMMC_CR_RESPTYP_Msk)
+ | SDMMC_CR_CMDIDX(driver->cmd.bCmd) | SDMMC_CR_CMDTYP_NORMAL
+ | SDMMC_CR_CMDICEN | SDMMC_CR_CMDCCEN;
+ eister = SDMMC_EISTER_BOOTAE | SDMMC_EISTER_TUNING | SDMMC_EISTER_ADMA
+ | SDMMC_EISTER_ACMD | SDMMC_EISTER_CURLIM | SDMMC_EISTER_DATEND
+ | SDMMC_EISTER_DATCRC | SDMMC_EISTER_DATTEO | SDMMC_EISTER_CMDIDX
+ | SDMMC_EISTER_CMDEND | SDMMC_EISTER_CMDCRC | SDMMC_EISTER_CMDTEO;
+
+ if (driver->cmd.cmdOp.bmBits.odON)
+ mc1r |= SDMMC_MC1R_OPD;
+
+ switch (driver->cmd.cmdOp.bmBits.respType) {
+
+ case 2:
+ cr |= SDMMC_CR_RESPTYP_RL136;
+ /* R2 response doesn't include the command index */
+ eister &= ~SDMMC_EISTER_CMDIDX;
+ break;
+ case 3:
+ /* R3 response includes neither the command index nor the CRC */
+ eister &= ~(SDMMC_EISTER_CMDIDX | SDMMC_EISTER_CMDCRC);
+ case 1:
+ case 4:
+ if (driver->cmd.cmdOp.bmBits.respType == 4 && driver->cmd.cmdOp.bmBits.ioCmd)
+ /* SDIO R4 response includes neither the command index nor the CRC */
+ eister &= ~(SDMMC_EISTER_CMDIDX | SDMMC_EISTER_CMDCRC);
+ case 5:
+ case 6:
+ case 7:
+ cr |= driver->cmd.cmdOp.bmBits.checkBsy ? SDMMC_CR_RESPTYP_RL48BUSY : SDMMC_CR_RESPTYP_RL48;
+ break;
+ default:
+ /* No response, ignore response time-out error */
+ cr |= SDMMC_CR_RESPTYP_NORESP;
+ eister &= ~SDMMC_EISTER_CMDTEO;
+ break;
+
+ }
+
+ if (stop_xfer) {
+ tmr |= SDMMC_TMR_MSBSEL | SDMMC_TMR_BCEN;
+ /* TODO consider BGCR:STPBGR (pause) */
+ /* TODO in case of SDIO consider CR:CMDTYP = ABORT */
+ /* Ignore data errors */
+ eister = eister & ~SDMMC_EISTER_ADMA & ~SDMMC_EISTER_DATEND
+ & ~SDMMC_EISTER_DATCRC & ~SDMMC_EISTER_DATTEO;
+ }
+ else if (has_data) {
+ cr |= SDMMC_CR_DPSEL;
+ tmr |= driver->cmd.cmdOp.bmBits.xfrData == SDMMC_CMD_TX
+ ? SDMMC_TMR_DTDSEL_WR : SDMMC_TMR_DTDSEL_RD;
+ if (blk_count_prefix)
+ tmr = (tmr & ~SDMMC_TMR_ACMDEN_Msk)
+ | SDMMC_TMR_ACMDEN_ACMD23;
+ else if (stop_xfer_suffix)
+ tmr = (tmr & ~SDMMC_TMR_ACMDEN_Msk)
+ | SDMMC_TMR_ACMDEN_ACMD12;
+ /* TODO check if this is fine for SDIO too (byte or block transfer) (driver->cmd.cmdOp.bmBits.ioCmd, driver->cmd.wBlockSize) */
+ if (multiple_xfer || driver->cmd.wNbBlocks > 1)
+ tmr |= SDMMC_TMR_MSBSEL | SDMMC_TMR_BCEN;
+ if (use_dma)
+ tmr |= SDMMC_TMR_DMAEN;
+ }
+
+ /* Wait for the CMD and DATn lines to be ready. If a previous command
+ * is still being processed, mind the status flags it may raise. */
+ mask = SDMMC_PSR_CMDINHC;
+ if (has_data || (driver->cmd.cmdOp.bmBits.checkBsy && !stop_xfer))
+ mask |= SDMMC_PSR_CMDINHD;
+
+ while (regs->SDMMC_PSR & mask) ;
+
+ /* Enable normal interrupts */
+ regs->SDMMC_NISTER |= SDMMC_NISTER_BRDRDY | SDMMC_NISTER_BWRRDY
+ | SDMMC_NISTER_TRFC | SDMMC_NISTER_CMDC;
+
+ osalDbgCheck(!(regs->SDMMC_NISTER & SDMMC_NISTR_CUSTOM_EVT));
+ /* Enable error interrupts */
+
+ regs->SDMMC_EISTER = eister;
+ /* Clear all interrupt status flags */
+ regs->SDMMC_NISTR = SDMMC_NISTR_ERRINT | SDMMC_NISTR_BOOTAR
+ | SDMMC_NISTR_CINT | SDMMC_NISTR_CREM | SDMMC_NISTR_CINS
+ | SDMMC_NISTR_BRDRDY | SDMMC_NISTR_BWRRDY | SDMMC_NISTR_DMAINT
+ | SDMMC_NISTR_BLKGE | SDMMC_NISTR_TRFC | SDMMC_NISTR_CMDC;
+
+ regs->SDMMC_EISTR = SDMMC_EISTR_BOOTAE | SDMMC_EISTR_TUNING
+ | SDMMC_EISTR_ADMA | SDMMC_EISTR_ACMD | SDMMC_EISTR_CURLIM
+ | SDMMC_EISTR_DATEND | SDMMC_EISTR_DATCRC | SDMMC_EISTR_DATTEO
+ | SDMMC_EISTR_CMDIDX | SDMMC_EISTR_CMDEND | SDMMC_EISTR_CMDCRC
+ | SDMMC_EISTR_CMDTEO;
+
+ /* Issue the command */
+ if (has_data) {
+ if (blk_count_prefix)
+ regs->SDMMC_SSAR = SDMMC_SSAR_ARG2(driver->cmd.wNbBlocks);
+
+ if (use_dma)
+ regs->SDMMC_ASA0R = SDMMC_ASA0R_ADMASA((uint32_t)driver->config->dma_table);
+
+ regs->SDMMC_BSR = (regs->SDMMC_BSR & ~SDMMC_BSR_BLKSIZE_Msk) | SDMMC_BSR_BLKSIZE(driver->cmd.wBlockSize);
+ }
+
+ if (stop_xfer)
+ regs->SDMMC_BCR = SDMMC_BCR_BLKCNT(0);
+ else if (has_data && (multiple_xfer || driver->cmd.wNbBlocks > 1))
+ regs->SDMMC_BCR = SDMMC_BCR_BLKCNT(driver->cmd.wNbBlocks);
+
+ regs->SDMMC_ARG1R = driver->cmd.dwArg;
+
+ if (has_data || stop_xfer)
+ regs->SDMMC_TMR = tmr;
+
+ regs->SDMMC_MC1R = mc1r;
+ regs->SDMMC_CR = cr;
+
+ /* In the case of Auto CMD12, we'll need to generate an extra event.
+ * Have our Timer/Counter ready for this. */
+ if (has_data && stop_xfer_suffix) {
+ /* Considering the multiple block read mode,
+ * 1. Assuming Transfer Complete is raised upon successful
+ * reception of the End bit of the last data packet,
+ * 2. A SD/eMMC protocol analyzer shows that the CMD12 command
+ * token is fully transmitted 1 or 2 device clock cycles
+ * later,
+ * 3. The device may take up to 64 clock cycles (NCR) before
+ * initiating the CMD12 response token,
+ * 4. The code length of the CMD12 response token (R1) is 48
+ * bits, hence 48 device clock cycles.
+ * The sum of the above timings is the maximum time CMD12 will
+ * take to complete. */
+
+ cycles = pmc_get_peripheral_clock(driver->tctimer_id) / (driver->dev_freq / (2ul + 64ul + 48ul));
+
+ //TRACE_1("[command] has_data wait %d cycles\r\n",cycles);
+ /* The Timer operates with RC >= 1 */
+ driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_RC = max_u32(cycles, 1);
+
+ }
+ /* With SD devices, the 8-cycle function switching period will apply,
+ * further to both SWITCH_FUNC and GO_IDLE_STATE commands.
+ * Note that MMC devices don't require this fixed delay, but regarding
+ * GO_IDLE_STATE we have no mean to filter the MMC requests out. */
+ else if (wait_switch) {
+
+ cycles = pmc_get_peripheral_clock(driver->tctimer_id) / (driver->dev_freq / 8ul);
+ //TRACE_1("[command] wait_switch %d cycles\r\n",cycles);
+ driver->config->tctimer->TC_CHANNEL[driver->config->tc_chan].TC_RC = max_u32(cycles, 1);
+
+ }
+ if (!driver->use_polling) {
+ regs->SDMMC_NISIER |= SDMMC_NISIER_BRDRDY | SDMMC_NISIER_BWRRDY | SDMMC_NISIER_TRFC | SDMMC_NISIER_CMDC | SDMMC_NISIER_CINT;
+ regs->SDMMC_EISIER = eister;
+ }
+
+ //TRACE_1("[command] finish %d OK\r\n",driver->cmd.bCmd);
+ return SDMMC_OK;
+ }
+
+
+
+ /**
+ * Here is the fSdmmcIOCtrl-type callback.
+ * IO control functions.
+ * \param _set Pointer to driver instance data (struct sdmmc_set).
+ * \param bCtl IO control code.
+ * \param param IO control parameter. Optional, depends on the IO control code.
+ * \return Return code, from the eSDMMC_RC enumeration.
+ */
+ uint32_t sdmmc_device_control(SdmmcDriver *driver, uint32_t bCtl)
+ {
+ //osalDbgCheck(driver);
+
+ //struct sdmmc_set *set = (struct sdmmc_set *)_set;
+ uint32_t rc = SDMMC_OK;
+ //uint32_t*param_u32 = (uint32_t *)param;
+ uint8_t byte;
+
+ //#if TRACE_LEVEL >= TRACE_LEVEL_DEBUG
+ if (bCtl != SDMMC_IOCTL_BUSY_CHECK && bCtl != SDMMC_IOCTL_GET_DEVICE) {
+ TRACE_2("SDMMC_IOCTL_%s(%lu)\n\r", SD_StringifyIOCtrl(bCtl),driver->control_param );
+ }
+ //#endif
+
+ switch (bCtl) {
+ case SDMMC_IOCTL_GET_DEVICE:
+
+ if ((driver->regs->SDMMC_CA0R & SDMMC_CA0R_SLTYPE_Msk) == SDMMC_CA0R_SLTYPE_EMBEDDED)
+ driver->control_param = 1;
+ else
+ driver->control_param = driver->regs->SDMMC_PSR & SDMMC_PSR_CARDINS ? 1 : 0;
+ break;
+
+ case SDMMC_IOCTL_GET_WP:
+
+ if ((driver->regs->SDMMC_CA0R & SDMMC_CA0R_SLTYPE_Msk) == SDMMC_CA0R_SLTYPE_EMBEDDED)
+ driver->control_param = 1;
+ else
+ driver->control_param = driver->regs->SDMMC_PSR & SDMMC_PSR_WRPPL ? 1 : 0;
+ break;
+
+ case SDMMC_IOCTL_POWER:
+
+ if (driver->control_param > SDMMC_PWR_STD_VDD_LOW_IO)
+ return SDMMC_PARAM;
+ if (driver->control_param == SDMMC_PWR_OFF)
+ rc = unplug_device(driver);
+ else if (driver->control_param == SDMMC_PWR_STD_VDD_LOW_IO && !(driver->regs->SDMMC_CA0R & SDMMC_CA0R_V18VSUP))
+ return SDMMC_PARAM;
+ else {
+ /* Power the device on, or change signaling level.
+ * This can't be done without configuring the timing
+ * mode at the same time. */
+ byte = driver->tim_mode;
+ if ((driver->regs->SDMMC_CA0R & (SDMMC_CA0R_V18VSUP| SDMMC_CA0R_V30VSUP| SDMMC_CA0R_V33VSUP)) != SDMMC_CA0R_V18VSUP) {
+ if (driver->control_param == SDMMC_PWR_STD_VDD_LOW_IO) {
+ if (byte < SDMMC_TIM_SD_DS)
+ byte = SDMMC_TIM_MMC_HS200;
+ else if (byte < SDMMC_TIM_SD_SDR12)
+ byte = SDMMC_TIM_SD_SDR12;
+ }
+ else {
+ if (byte > SDMMC_TIM_SD_HS)
+ byte = SDMMC_TIM_SD_DS;
+ else if (byte > SDMMC_TIM_MMC_HS_DDR
+ && byte < SDMMC_TIM_SD_DS)
+ byte = SDMMC_TIM_MMC_BC;
+ }
+ }
+ rc = sdmmc_set_speed_mode(driver, byte, true);
+ }
+ break;
+
+ case SDMMC_IOCTL_RESET:
+ /* Release the device. The device may have been removed. */
+ rc = unplug_device(driver);
+ break;
+
+ case SDMMC_IOCTL_GET_BUSMODE:
+ byte = sdmmc_get_bus_width(driver);
+ driver->control_param = byte;
+ break;
+
+ case SDMMC_IOCTL_SET_BUSMODE:
+ if (driver->control_param > 0xff)
+ return SDMMC_PARAM;
+ rc = sdmmc_set_bus_width(driver, driver->control_param);
+ TRACE_1("Using a %u-bit data bus\n\r", sdmmc_get_bus_width(driver));
+ break;
+
+ case SDMMC_IOCTL_GET_HSMODE:
+
+ if (driver->control_param > 0xff) {
+ driver->control_param = 0;
+ break;
+ }
+
+ byte = (uint8_t)driver->control_param;
+
+ if (byte == SDMMC_TIM_MMC_BC || byte == SDMMC_TIM_SD_DS) {
+ driver->control_param = 1;
+ }
+ else if ((byte == SDMMC_TIM_MMC_HS_SDR
+ || byte == SDMMC_TIM_MMC_HS_DDR || byte == SDMMC_TIM_SD_HS)
+ && driver->regs->SDMMC_CA0R & SDMMC_CA0R_HSSUP)
+ driver->control_param = 1;
+ else if (byte == SDMMC_TIM_MMC_HS200
+ && (driver->regs->SDMMC_CA0R & (SDMMC_CA0R_V18VSUP
+ | SDMMC_CA0R_V30VSUP | SDMMC_CA0R_V33VSUP))
+ == SDMMC_CA0R_V18VSUP
+ && driver->regs->SDMMC_CA1R & (SDMMC_CA1R_SDR50SUP
+ | SDMMC_CA1R_DDR50SUP | SDMMC_CA1R_SDR104SUP))
+ driver->control_param = 1;
+ /* TODO rely on platform code to get the data bus width to the
+ * SD slot, and deny UHS-I timing modes if the DAT[3:0] signals
+ * are not all routed. */
+ else if ((byte == SDMMC_TIM_SD_SDR12
+ || byte == SDMMC_TIM_SD_SDR25)
+ && driver->regs->SDMMC_CA0R & SDMMC_CA0R_V18VSUP
+ && driver->regs->SDMMC_CA1R & (SDMMC_CA1R_SDR50SUP
+ | SDMMC_CA1R_DDR50SUP | SDMMC_CA1R_SDR104SUP))
+ driver->control_param= 1;
+ else if (byte == SDMMC_TIM_SD_SDR50
+ && driver->regs->SDMMC_CA0R & SDMMC_CA0R_V18VSUP
+ && driver->regs->SDMMC_CA1R & SDMMC_CA1R_SDR50SUP)
+ driver->control_param = 1;
+ else if (byte == SDMMC_TIM_SD_DDR50
+ && driver->regs->SDMMC_CA0R & SDMMC_CA0R_V18VSUP
+ && driver->regs->SDMMC_CA1R & SDMMC_CA1R_DDR50SUP)
+ driver->control_param = 1;
+ else if (byte == SDMMC_TIM_SD_SDR104
+ && driver->regs->SDMMC_CA0R & SDMMC_CA0R_V18VSUP
+ && driver->regs->SDMMC_CA1R & SDMMC_CA1R_SDR104SUP)
+ driver->control_param = 1;
+ else
+ driver->control_param = 0;
+ break;
+
+ case SDMMC_IOCTL_SET_HSMODE:
+
+ if (driver->control_param > 0xff)
+ return SDMMC_PARAM;
+ rc = sdmmc_set_speed_mode(driver, (uint8_t)driver->control_param, false);
+ driver->control_param= driver->tim_mode;
+ break;
+
+ case SDMMC_IOCTL_SET_CLOCK:
+
+ if (driver->control_param == 0)
+ return SDMMC_PARAM;
+
+ sdmmc_set_device_clock(driver, driver->control_param);
+
+ TRACE_1("Clocking the device at %lu Hz\n\r", driver->dev_freq);
+ if (driver->dev_freq > 95000000ul
+ && (driver->tim_mode == SDMMC_TIM_MMC_HS200
+ || driver->tim_mode == SDMMC_TIM_SD_SDR104
+ || (driver->tim_mode == SDMMC_TIM_SD_SDR50
+ && driver->regs->SDMMC_CA1R & SDMMC_CA1R_TSDR50)))
+ rc = tuneSampling(driver);
+ /* TODO setup periodic re-tuning */
+ if (driver->dev_freq != driver->control_param) {
+ rc = rc == SDMMC_OK ? SDMMC_CHANGED : rc;
+ driver->control_param = driver->dev_freq;
+ }
+ break;
+
+ case SDMMC_IOCTL_SET_LENPREFIX:
+
+ driver->use_set_blk_cnt = driver->control_param ? true : false;
+ driver->control_param = driver->use_set_blk_cnt ? 1 : 0;
+ break;
+
+ case SDMMC_IOCTL_GET_XFERCOMPL:
+
+ driver->control_param = 1;
+ break;
+
+ case SDMMC_IOCTL_BUSY_CHECK:
+
+ if (driver->state == MCID_OFF)
+ driver->control_param = 0;
+ else
+ {
+
+ if (driver->use_polling) {
+ sdmmc_device_poll(driver);
+ }
+ if (driver->state == MCID_CMD) {
+ driver->control_param =1;
+ }
+ else {
+ driver->control_param = 0;
+ }
+
+ }
+ break;
+
+ case SDMMC_IOCTL_CANCEL_CMD:
+ if (driver->state == MCID_OFF)
+ rc = SDMMC_STATE;
+ else
+ rc = CancelCommand(driver);
+ break;
+
+ case SDMMC_IOCTL_GET_CLOCK:
+ case SDMMC_IOCTL_SET_BOOTMODE:
+ case SDMMC_IOCTL_GET_BOOTMODE:
+ default:
+ rc = SDMMC_NOT_SUPPORTED;
+ break;
+ }
+ //#if TRACE_LEVEL >= TRACE_LEVEL_ERROR
+ if (rc != SDMMC_OK && rc != SDMMC_CHANGED
+ && bCtl != SDMMC_IOCTL_BUSY_CHECK) {
+ TRACE_2("SDMMC_IOCTL_%s ended with %s\n\r",SD_StringifyIOCtrl(bCtl), SD_StringifyRetCode(rc));
+ }
+ //#endif
+ return rc;
+ }
+
+ void sdmmc_device_sleep(SdmmcDriver *driver,uint32_t t,uint32_t m)
+ {
+ systime_t time, end, now;
+ uint32_t f = 0;
+
+ (void)driver;
+
+ time = chVTGetSystemTimeX();
+
+ if (m==1)
+ end = time + TIME_MS2I(t);
+ else if (m==2)
+ end = time + TIME_US2I(t);
+ else
+ end = time + (systime_t)t;
+
+ do {
+ //chSysLock();
+ now = chVTTimeElapsedSinceX(time);
+ //chSysUnlock();
+ if (now >= end) {
+ f = 1;
+ }
+
+ } while (!f);
+
+ }
+
+ void sdmmc_device_startTimeCount(SdmmcDriver *driver)
+ {
+ if (driver->timeout_elapsed != -1) {
+ driver->time = chVTGetSystemTimeX();
+ driver->now = driver->time;
+ }
+ }
+
+ void sdmmc_device_checkTimeCount(SdmmcDriver *driver)
+ {
+ if (driver->timeout_elapsed != -1) {
+ // chSysLock();
+ driver->timeout_elapsed = 0;
+ driver->now = chVTTimeElapsedSinceX( driver->time);
+ if (driver->now >= driver->timeout_ticks ) {
+ driver->timeout_elapsed = 1;
+ }
+ //chSysUnlock();
+ }
+ }
+
+ static void calibrate_zout(Sdmmc * regs)
+ {
+ uint32_t calcr;
+
+ /* FIXME find out if this operation should be carried with PCR:SDBPWR
+ * set and/or the device clock started. */
+
+ /* CALCR:CNTVAL has been configured by sdmmc_initialize() */
+ regs->SDMMC_CALCR |= SDMMC_CALCR_EN;
+ do
+ calcr = regs->SDMMC_CALCR;
+ while (calcr & SDMMC_CALCR_EN);
+ //trace_debug("Output Z calibr. CALN=%lu CALP=%lu\n\r",
+ // (calcr & SDMMC_CALCR_CALN_Msk) >> SDMMC_CALCR_CALN_Pos,
+ // (calcr & SDMMC_CALCR_CALP_Msk) >> SDMMC_CALCR_CALP_Pos);
+ }
+
+ void reset_peripheral(SdmmcDriver *driver)
+ {
+
+ uint32_t calcr;
+ uint8_t mc1r, tcr;
+
+ /* First, save the few settings we'll want to restore. */
+ mc1r = driver->regs->SDMMC_MC1R;
+ tcr = driver->regs->SDMMC_TCR;
+ calcr = driver->regs->SDMMC_CALCR;
+
+ /* Reset our state variables to match reset values of the registers */
+ driver->tim_mode = driver->tim_mode >= SDMMC_TIM_SD_DS ? SDMMC_TIM_SD_DS
+ : SDMMC_TIM_MMC_BC;
+
+ /* Reset the peripheral. This will reset almost all registers. */
+ driver->regs->SDMMC_SRR |= SDMMC_SRR_SWRSTALL;
+ while (driver->regs->SDMMC_SRR & SDMMC_SRR_SWRSTALL) ;
+
+ /* Restore specific register fields */
+ if (mc1r & SDMMC_MC1R_FCD)
+ driver->regs->SDMMC_MC1R |= SDMMC_MC1R_FCD;
+ driver->regs->SDMMC_TCR = (driver->regs->SDMMC_TCR & ~SDMMC_TCR_DTCVAL_Msk)
+ | (tcr & SDMMC_TCR_DTCVAL_Msk);
+ driver->regs->SDMMC_CALCR = (driver->regs->SDMMC_CALCR & ~SDMMC_CALCR_CNTVAL_Msk
+ & ~SDMMC_CALCR_TUNDIS) | (calcr & SDMMC_CALCR_CNTVAL_Msk);
+
+ /* Apply our unconditional custom settings */
+ /* When using DMA, use the 32-bit Advanced DMA 2 mode */
+ driver->regs->SDMMC_HC1R = (driver->regs->SDMMC_HC1R & ~SDMMC_HC1R_DMASEL_Msk)
+ | SDMMC_HC1R_DMASEL_ADMA32;
+ /* Configure maximum AHB burst size */
+ driver->regs->SDMMC_ACR = (driver->regs->SDMMC_ACR & ~SDMMC_ACR_BMAX_Msk)
+ | SDMMC_ACR_BMAX_INCR16;
+ }
+
+
+
+ void sdmmc_set_capabilities(
+ Sdmmc * regs,
+ uint32_t caps0, uint32_t caps0_mask,
+ uint32_t caps1, uint32_t caps1_mask)
+ {
+ osalDbgCheck((caps0 & caps0_mask) == caps0);
+ osalDbgCheck((caps1 & caps1_mask) == caps1);
+
+ caps0 = (regs->SDMMC_CA0R & ~caps0_mask) | (caps0 & caps0_mask);
+ caps1 = (regs->SDMMC_CA1R & ~caps1_mask) | (caps1 & caps1_mask);
+
+ regs->SDMMC_CACR = SDMMC_CACR_KEY(0x46) | SDMMC_CACR_CAPWREN;
+ if (regs->SDMMC_CA0R != caps0)
+ regs->SDMMC_CA0R = caps0;
+ if (regs->SDMMC_CA1R != caps1)
+ regs->SDMMC_CA1R = caps1;
+ regs->SDMMC_CACR = SDMMC_CACR_KEY(0x46) | 0;
+ }
+
+ /**
+ * \brief Retrieve command response from the SDMMC peripheral.
+ */
+ static void sdmmc_get_response(SdmmcDriver *driver, sSdmmcCommand *cmd, bool complete, uint32_t *out)
+ {
+ //osalDbgCheck(set);
+ osalDbgCheck(cmd);
+ osalDbgCheck(cmd->cmdOp.bmBits.respType <= 7);
+ osalDbgCheck(out);
+
+ const bool first_call = driver->resp_len == 0;
+ const bool has_data = cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_TX
+ || cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_RX;
+ uint32_t resp;
+ uint8_t ix;
+
+ if (first_call) {
+ switch (cmd->cmdOp.bmBits.respType) {
+ case 2:
+ /* R2 response is 120-bit long, split in
+ * 32+32+32+24 bits this way:
+ * RR[0] = R[ 39: 8]
+ * RR[1] = R[ 71: 40]
+ * RR[2] = R[103: 72]
+ * RR[3] = R[127:104]
+ * Shift data the way libsdmmc expects it,
+ * that is:
+ * pResp[0] = R[127: 96]
+ * pResp[1] = R[ 95: 64]
+ * pResp[2] = R[ 63: 32]
+ * pResp[3] = R[ 31: 0]
+ * The CRC7 and the end bit aren't provided,
+ * just hard-code their default values. */
+ out[3] = 0x000000ff;
+ for (ix = 0; ix < 4; ix++) {
+ resp = driver->regs->SDMMC_RR[ix];
+ if (ix < 3)
+ out[2 - ix] = resp >> 24 & 0xff;
+ out[3 - ix] |= resp << 8 & 0xffffff00;
+ }
+ driver->resp_len = 4;
+ break;
+ case 1: case 3: case 4: case 5: case 6: case 7:
+ /* The nominal response is 32-bit long */
+ out[0] = driver->regs->SDMMC_RR[0];
+ driver->resp_len = 1;
+ break;
+ case 0:
+ default:
+ break;
+ }
+ }
+
+ if (has_data && (cmd->bCmd == 18 || cmd->bCmd == 25) && ((first_call
+ && driver->use_set_blk_cnt) || (complete && !driver->use_set_blk_cnt))) {
+ resp = driver->regs->SDMMC_RR[3];
+ #if 0
+ trace_debug("Auto CMD%d returned status 0x%lx\n\r",
+ set->use_set_blk_cnt ? 23 : 12, resp);
+ #endif
+ if (!driver->use_set_blk_cnt)
+ /* We return a single response to the application: the
+ * device status returned by CMD18 or CMD25, combined
+ * with the device status just returned by Auto CMD12.
+ * Retain the status bits from only CMD18 or CMD25, and
+ * combine the exception bits from both. */
+ out[0] |= resp & ~STAT_DEVICE_IS_LOCKED
+ & ~STAT_CARD_ECC_DISABLED & ~STAT_CURRENT_STATE
+ & ~STAT_READY_FOR_DATA & ~STAT_EXCEPTION_EVENT
+ & ~STAT_APP_CMD;
+ //#ifndef NDEBUG
+ // resp = (resp & STAT_CURRENT_STATE) >> 9;
+ // if (driver->config->use_set_blk_cnt && resp != STATE_TRANSFER)
+ // trace_warning("Auto CMD23 returned state %lx\n\r", resp);
+ // else if (!driver->config->use_set_blk_cnt && cmd->bCmd == 18
+ // && resp != STATE_SENDING_DATA)
+ // trace_warning("CMD18 switched to state %lx\n\r", resp);
+ // else if (!driver->config->use_set_blk_cnt && cmd->bCmd == 25
+ /// && resp != STATE_RECEIVE_DATA && resp != STATE_PROGRAMMING)
+ // trace_warning("CMD25 switched to state %lx\n\r", resp);
+ //#endif
+ }
+ }
+
+static bool sdmmc_is_busy(SdmmcDriver *driver)
+{
+ //osalDbgCheck(driver->state != MCID_OFF);
+
+ if (driver->use_polling)
+ sdmmc_device_poll(driver);
+ if (driver->state == MCID_CMD)
+ return true;
+ return false;
+}
+
+
+static uint8_t sdmmc_build_dma_table( SdmmcDriver *driver )
+{
+ //assert(set);
+ //assert(set->table);
+ //assert(set->table_size);
+ //assert(cmd->pData);
+ //assert(cmd->wBlockSize);
+ //assert(cmd->wNbBlocks);
+ sSdmmcCommand *cmd = &driver->cmd;
+ uint32_t *line = NULL;
+ uint32_t data_len = (uint32_t)cmd->wNbBlocks
+ * (uint32_t)cmd->wBlockSize;
+ uint32_t ram_addr = (uint32_t)cmd->pData;
+ uint32_t ram_bound = ram_addr + data_len;
+ uint32_t line_ix, line_cnt;
+ uint8_t rc = SDMMC_OK;
+
+#if 0
+ trace_debug("Configuring DMA for a %luB transfer %s %p\n\r",
+ data_len, cmd->cmdOp.bmBits.xfrData == SDMMC_CMD_TX ? "from" : "to",
+ cmd->pData);
+#endif
+ /* Verify that cmd->pData is word-aligned */
+ if ((uint32_t)cmd->pData & 0x3)
+ return SDMMC_PARAM;
+ /* Compute the size of the descriptor table for this transfer */
+ line_cnt = (data_len - 1 + SDMMC_DMADL_TRAN_LEN_MAX)/ SDMMC_DMADL_TRAN_LEN_MAX;
+ /* If it won't fit into the allocated buffer, resize the transfer */
+ if (line_cnt > driver->config->dma_table_size) {
+ line_cnt = driver->config->dma_table_size;
+ data_len = line_cnt * SDMMC_DMADL_TRAN_LEN_MAX;
+ data_len /= cmd->wBlockSize;
+ if (data_len == 0)
+ return SDMMC_NOT_SUPPORTED;
+ cmd->wNbBlocks = (uint16_t)data_len;
+ data_len *= cmd->wBlockSize;
+ ram_bound = ram_addr + data_len;
+ rc = SDMMC_CHANGED;
+ }
+ /* Fill the table */
+ for (line_ix = 0, line = driver->config->dma_table; line_ix < line_cnt;
+ line_ix++, line += SDMMC_DMADL_SIZE) {
+ if (line_ix + 1 < line_cnt) {
+ line[0] = SDMMC_DMA0DL_LEN_MAX
+ | SDMMC_DMA0DL_ATTR_ACT_TRAN
+ | SDMMC_DMA0DL_ATTR_VALID;
+ line[1] = SDMMC_DMA1DL_ADDR(ram_addr);
+ ram_addr += SDMMC_DMADL_TRAN_LEN_MAX;
+ }
+ else {
+ line[0] = ram_bound - ram_addr
+ < SDMMC_DMADL_TRAN_LEN_MAX
+ ? SDMMC_DMA0DL_LEN(ram_bound - ram_addr)
+ : SDMMC_DMA0DL_LEN_MAX;
+ line[0] |= SDMMC_DMA0DL_ATTR_ACT_TRAN
+ | SDMMC_DMA0DL_ATTR_END | SDMMC_DMA0DL_ATTR_VALID;
+ line[1] = SDMMC_DMA1DL_ADDR(ram_addr);
+ }
+#if 0
+ trace_debug("DMA descriptor: %luB @ 0x%lx%c\n\r",
+ (line[0] & SDMMC_DMA0DL_LEN_Msk) >> SDMMC_DMA0DL_LEN_Pos,
+ line[1], line[0] & SDMMC_DMA0DL_ATTR_END ? '.' : ' ');
+#endif
+ }
+ /* Clean the underlying cache lines, to ensure the DMA gets our table
+ * when it reads from RAM.
+ * CPU access to the table is write-only, peripheral/DMA access is read-
+ * only, hence there is no need to invalidate. */
+ cacheCleanRegion(driver->config->dma_table, (uint32_t)line - (uint32_t)driver->config->dma_table);
+
+ return rc;
+}
+
+static uint8_t unplug_device(SdmmcDriver *driver)
+{
+ //osalDbgCheck(set);
+
+ Sdmmc *regs = driver->regs;
+ uint32_t usec = 0;
+ uint8_t mc1r;
+
+ //trace_debug("Release and power the device off\n\r");
+ if (driver->state == MCID_CMD)
+ CancelCommand(driver);
+
+ /* Hardware-reset the e.MMC, move it to the pre-idle state.
+ * Note that this will only be effective on systems where
+ * 1) the RST_n e.MMC input is wired to the SDMMCx_RSTN PIO, and
+ * 2) the hardware reset functionality of the device has been
+ * enabled by software (!) Refer to ECSD register byte 162. */
+ /* Generate a pulse on SDMMCx_RSTN. Satisfy tRSTW >= 1 usec.
+ * The timer driver can't cope with periodic interrupts triggered as
+ * frequently as one interrupt per microsecond. Extend to 10 usec. */
+ mc1r = regs->SDMMC_MC1R;
+ regs->SDMMC_MC1R = mc1r | SDMMC_MC1R_RSTN;
+ t_usleep(driver,10);
+ regs->SDMMC_MC1R = mc1r;
+ /* Wait for either tRSCA = 200 usec or 74 device clock cycles, as per
+ * the e.MMC Electrical Standard. */
+ if (driver->dev_freq != 0)
+ usec = ROUND_INT_DIV(74 * 1000000UL, driver->dev_freq);
+ usec = max_u32(usec, 200);
+ t_usleep(driver,usec);
+
+ /* Stop both the output clock and the SDMMC internal clock */
+ regs->SDMMC_CCR &= ~(SDMMC_CCR_SDCLKEN | SDMMC_CCR_INTCLKEN);
+ driver->dev_freq = 0;
+ /* Cut the power rail supplying signals to/from the device */
+ regs->SDMMC_PCR &= ~SDMMC_PCR_SDBPWR;
+ /* Reset the peripheral. This will reset almost all registers. */
+ reset_peripheral(driver);
+
+ driver->state = MCID_OFF;
+ return SDMMC_OK;
+}
+
+
+/**
+ * \brief Switch to the specified timing mode
+ * \note Since HC2R:VS18EN and HC2R:UHSMS fields depend on each other, this
+ * function simultaneously updates the timing mode and the electrical state of
+ * host I/Os.
+ * \param set Pointer to the driver instance data
+ * \param mode The new timing mode
+ * \param verify When switching from high to low signaling level, expect
+ * the host input levels driven by the device to conform to the VOLTAGE_SWITCH
+ * standard sequence.
+ * \return A \ref sdmmc_rc result code
+ */
+static uint8_t sdmmc_set_speed_mode(SdmmcDriver *driver, uint8_t mode,bool verify)
+{
+ //osalDbgCheck(set);
+
+ Sdmmc *regs = driver->regs;
+ const uint32_t caps = regs->SDMMC_CA0R;
+ /* Deviation from the SD Host Controller Specification: we use the
+ * Voltage Support capabilities to indicate the supported signaling
+ * levels (VCCQ), rather than the power supply voltage (VCC). */
+ const bool perm_low_sig = (caps & (SDMMC_CA0R_V18VSUP
+ | SDMMC_CA0R_V30VSUP | SDMMC_CA0R_V33VSUP)) == SDMMC_CA0R_V18VSUP;
+ uint32_t usec = 0;
+ uint16_t hc2r_prv, hc2r;
+ uint8_t rc = SDMMC_OK, hc1r_prv, hc1r, mc1r_prv, mc1r, pcr_prv, pcr;
+ bool toggle_sig_lvl, low_sig, dev_clk_on;
+
+ if ((mode > SDMMC_TIM_MMC_HS200 && mode < SDMMC_TIM_SD_DS)
+ || mode > SDMMC_TIM_SD_SDR104)
+ return SDMMC_PARAM;
+ if ((mode == SDMMC_TIM_MMC_HS200
+ || (mode >= SDMMC_TIM_SD_SDR12 && mode <= SDMMC_TIM_SD_SDR104))
+ && !(caps & SDMMC_CA0R_V18VSUP))
+ return SDMMC_PARAM;
+
+#ifndef NDEBUG
+ /* FIXME The datasheet is unclear about CCR:DIV restriction when the MMC
+ * timing mode is High Speed DDR */
+ if ((mode == SDMMC_TIM_MMC_HS_SDR || mode == SDMMC_TIM_MMC_HS_DDR
+ || mode == SDMMC_TIM_SD_HS) && !(regs->SDMMC_CCR
+ & (SDMMC_CCR_USDCLKFSEL_Msk | SDMMC_CCR_SDCLKFSEL_Msk))) {
+ //trace_error("Incompatible with the current clock config\n\r");
+ return SDMMC_STATE;
+ }
+#endif
+
+ driver->state = (driver->state == MCID_OFF) ? MCID_IDLE : driver->state;
+
+ mc1r = mc1r_prv = regs->SDMMC_MC1R;
+ hc1r = hc1r_prv = regs->SDMMC_HC1R;
+ hc2r = hc2r_prv = regs->SDMMC_HC2R;
+ pcr = pcr_prv = regs->SDMMC_PCR;
+ mc1r = (mc1r & ~SDMMC_MC1R_DDR)
+ | (mode == SDMMC_TIM_MMC_HS_DDR ? SDMMC_MC1R_DDR : 0);
+ hc1r = (hc1r & ~SDMMC_HC1R_HSEN) | (mode == SDMMC_TIM_MMC_HS_SDR
+ || mode == SDMMC_TIM_SD_HS ? SDMMC_HC1R_HSEN : 0);
+ hc2r = hc2r & ~SDMMC_HC2R_DRVSEL_Msk & ~SDMMC_HC2R_VS18EN
+ & ~SDMMC_HC2R_UHSMS_Msk;
+ if (mode == SDMMC_TIM_MMC_HS200
+ || (mode >= SDMMC_TIM_SD_SDR12 && mode <= SDMMC_TIM_SD_SDR104))
+ hc2r |= SDMMC_HC2R_VS18EN;
+ if (mode == SDMMC_TIM_MMC_HS200 || mode == SDMMC_TIM_SD_SDR104)
+ hc2r |= SDMMC_HC2R_UHSMS_SDR104;
+ else if (mode == SDMMC_TIM_SD_SDR12)
+ hc2r |= SDMMC_HC2R_UHSMS_SDR12;
+ else if (mode == SDMMC_TIM_SD_SDR25)
+ hc2r |= SDMMC_HC2R_UHSMS_SDR25;
+ else if (mode == SDMMC_TIM_SD_SDR50)
+ hc2r |= SDMMC_HC2R_UHSMS_SDR50;
+ else if (mode == SDMMC_TIM_SD_DDR50)
+ hc2r |= SDMMC_HC2R_UHSMS_DDR50;
+ /* Use the fixed clock when sampling data. Except if we keep using
+ * a 100+ MHz device clock. */
+ if (driver->dev_freq <= 95000000ul || (mode != SDMMC_TIM_MMC_HS200
+ && mode != SDMMC_TIM_SD_SDR104 && (mode != SDMMC_TIM_SD_SDR50
+ || !(regs->SDMMC_CA1R & SDMMC_CA1R_TSDR50))))
+ hc2r &= ~SDMMC_HC2R_SCLKSEL;
+ /* On SAMA5D2-XULT when using 1.8V signaling, on host outputs choose
+ * Driver Type C, i.e. 66 ohm nominal output impedance.
+ * FIXME rely on platform code to retrieve the optimal host output
+ * Driver Type. It depends on board design. An oscilloscope should be
+ * set up to observe signal integrity, then among the driver types that
+ * meet rise and fall time requirements, the weakest should be selected.
+ */
+ if (hc2r & SDMMC_HC2R_VS18EN)
+ hc2r |= SDMMC_HC2R_DRVSEL_TYPEC;
+ pcr = (pcr & ~SDMMC_PCR_SDBVSEL_Msk) | SDMMC_PCR_SDBPWR;
+ low_sig = perm_low_sig || hc2r & SDMMC_HC2R_VS18EN;
+ if (low_sig)
+ pcr |= SDMMC_PCR_SDBVSEL_18V;
+ else
+ pcr |= caps & SDMMC_CA0R_V30VSUP ? SDMMC_PCR_SDBVSEL_30V
+ : SDMMC_PCR_SDBVSEL_33V;
+
+ if (hc2r == hc2r_prv && hc1r == hc1r_prv && mc1r == mc1r_prv
+ && pcr == pcr_prv)
+ goto End;
+ toggle_sig_lvl = pcr_prv & SDMMC_PCR_SDBPWR
+ && (pcr ^ pcr_prv) & SDMMC_PCR_SDBVSEL_Msk;
+ //if (!(pcr_prv & SDMMC_PCR_SDBPWR))
+ // trace_debug("Power the device on\n\r");
+ //else if (toggle_sig_lvl)
+ // trace_debug("Signaling level going %s\n\r",
+ // hc2r & SDMMC_HC2R_VS18EN ? "low" : "high");
+ if (verify && toggle_sig_lvl && hc2r & SDMMC_HC2R_VS18EN) {
+ /* Expect this call to follow the VOLTAGE_SWITCH command;
+ * allow 2 device clock periods before the device pulls the CMD
+ * and DAT[3:0] lines down */
+ if (driver->dev_freq != 0)
+ usec = ROUND_INT_DIV(2 * 1000000UL, driver->dev_freq);
+ usec = max_u32(usec, 10);
+ t_usleep(driver,usec);
+ if (regs->SDMMC_PSR & (SDMMC_PSR_CMDLL | SDMMC_PSR_DATLL_Msk))
+ rc = SDMMC_STATE;
+ }
+ /* Avoid generating glitches on the device clock */
+ dev_clk_on = regs->SDMMC_CCR & SDMMC_CCR_SDCLKEN
+ && (toggle_sig_lvl || hc2r_prv & SDMMC_HC2R_PVALEN
+ || hc2r != hc2r_prv);
+ if (dev_clk_on)
+ regs->SDMMC_CCR &= ~SDMMC_CCR_SDCLKEN;
+ if (toggle_sig_lvl)
+ /* Drive the device clock low, turn CMD and DATx high-Z */
+ regs->SDMMC_PCR = pcr & ~SDMMC_PCR_SDBPWR;
+
+ /* Now change the timing mode */
+ if (mc1r != mc1r_prv)
+ regs->SDMMC_MC1R = mc1r;
+ if (hc1r != hc1r_prv)
+ regs->SDMMC_HC1R = hc1r;
+ if (hc2r != hc2r_prv)
+ regs->SDMMC_HC2R = hc2r;
+ if (toggle_sig_lvl) {
+ /* Changing the signaling level. The SD Host Controller
+ * Specification requires the HW to stabilize the electrical
+ * levels within 5 ms, which equals 5 system ticks.
+ * Alternative: wait for tPRUL = 25 ms */
+ t_msleep(driver,5);
+ if (hc2r & SDMMC_HC2R_VS18EN
+ && !(regs->SDMMC_HC2R & SDMMC_HC2R_VS18EN))
+ rc = SDMMC_ERR;
+ }
+ if (pcr != pcr_prv)
+ regs->SDMMC_PCR = pcr;
+ if (verify && toggle_sig_lvl && hc2r & SDMMC_HC2R_VS18EN) {
+ t_msleep(driver,1);
+ if (regs->SDMMC_PSR & (SDMMC_PSR_CMDLL | SDMMC_PSR_DATLL_Msk))
+ rc = SDMMC_STATE;
+ }
+ if (dev_clk_on || (toggle_sig_lvl && hc2r & SDMMC_HC2R_VS18EN))
+ /* FIXME verify that current dev clock freq is 400 kHz */
+ regs->SDMMC_CCR |= SDMMC_CCR_SDCLKEN;
+ if (toggle_sig_lvl && hc2r & SDMMC_HC2R_VS18EN) {
+ /* Expect the device to release the CMD and DAT[3:0] lines
+ * within 1 ms */
+ t_msleep(driver,1);
+ if ((regs->SDMMC_PSR & (SDMMC_PSR_CMDLL | SDMMC_PSR_DATLL_Msk))
+ != (SDMMC_PSR_CMDLL | SDMMC_PSR_DATLL_Msk) && verify)
+ rc = SDMMC_STATE;
+ if (!dev_clk_on)
+ regs->SDMMC_CCR &= ~SDMMC_CCR_SDCLKEN;
+ }
+ //trace_debug("Using timing mode 0x%02x\n\r", mode);
+
+ regs->SDMMC_CALCR = (regs->SDMMC_CALCR & ~SDMMC_CALCR_ALWYSON)
+ | (low_sig ? SDMMC_CALCR_ALWYSON : 0);
+ if (low_sig || pcr != pcr_prv)
+ /* Perform the output calibration sequence */
+ calibrate_zout(driver->regs);
+ /* TODO in SDR12-50/DDR50 mode, schedule periodic re-calibration */
+
+End:
+ if (rc == SDMMC_OK)
+ driver->tim_mode = mode;
+ return rc;
+}
+
+
+
+
+uint8_t sdmmc_get_bus_width(SdmmcDriver *driver)
+{
+ //osalDbgCheck(set);
+
+ const uint8_t hc1r = driver->regs->SDMMC_HC1R;
+
+ if (hc1r & SDMMC_HC1R_EXTDW)
+ return 8;
+ else if (hc1r & SDMMC_HC1R_DW)
+ return 4;
+ else
+ return 1;
+}
+
+static uint8_t sdmmc_set_bus_width(SdmmcDriver *driver, uint8_t bits)
+{
+ //osalDbgCheck(set);
+
+ Sdmmc *regs = driver->regs;
+ uint8_t hc1r_prv, hc1r;
+
+ if (bits != 1 && bits != 4 && bits != 8)
+ return SDMMC_PARAM;
+ if (bits == 8 && !(regs->SDMMC_CA0R & SDMMC_CA0R_ED8SUP)) {
+ //trace_error("This slot doesn't support an 8-bit data bus\n\r");
+ return SDMMC_PARAM;
+ }
+ /* TODO in case of SD slots, rely on platform code to get the width of
+ * the data bus actually implemented on the board. In the meantime we
+ * assume DAT[3:0] are all effectively connected to the device. */
+
+ hc1r = hc1r_prv = regs->SDMMC_HC1R;
+ if (bits == 8 && hc1r & SDMMC_HC1R_EXTDW)
+ return SDMMC_OK;
+ else if (bits == 8)
+ hc1r |= SDMMC_HC1R_EXTDW;
+ else {
+ hc1r &= ~SDMMC_HC1R_EXTDW;
+ if (bits == 4)
+ hc1r |= SDMMC_HC1R_DW;
+ else
+ hc1r &= ~SDMMC_HC1R_DW;
+ if (hc1r == hc1r_prv)
+ return SDMMC_OK;
+ }
+ regs->SDMMC_HC1R = hc1r;
+ return SDMMC_OK;
+}
+
+
+ static uint8_t HwReset(SdmmcDriver *driver)
+ {
+ uint32_t rc;
+
+ driver->control_param = 0;
+
+ rc = sdmmc_device_control(driver, SDMMC_IOCTL_RESET);
+
+ return rc;
+ }
+
+ uint8_t HwPowerDevice(SdmmcDriver *drv, uint8_t nowSwitchOn)
+ {
+ uint32_t rc;
+
+ drv->control_param = nowSwitchOn;
+
+ rc = sdmmc_device_control(drv, SDMMC_IOCTL_POWER);
+
+ return rc;
+ }
+
+ uint8_t HwSetHsMode(SdmmcDriver *drv, uint8_t timingMode)
+ {
+ uint32_t rc;
+
+ drv->control_param = timingMode;
+
+ rc = sdmmc_device_control(drv, SDMMC_IOCTL_SET_HSMODE);
+
+ if ((rc == SDMMC_OK || rc == SDMMC_CHANGED)
+ && (drv->control_param > 0xff || drv->control_param != (uint32_t)timingMode))
+ rc = SDMMC_CHANGED;
+ return rc;
+ }
+
+ uint32_t HwSetBusWidth( SdmmcDriver *drv,uint8_t newWidth)
+ {
+ uint32_t rc;
+
+ drv->control_param = newWidth;
+
+ rc = sdmmc_device_control(drv, SDMMC_IOCTL_SET_BUSMODE);
+
+ return rc;
+ }
+
+ bool HwIsTimingSupported(SdmmcDriver *drv, uint8_t timingMode)
+ {
+ uint32_t rc;
+
+ drv->control_param = timingMode;
+
+ rc = sdmmc_device_control(drv, SDMMC_IOCTL_GET_HSMODE);
+
+ return rc == SDMMC_OK ? (drv->control_param ? true : false) : false;
+ }
+
+ uint8_t HwSetClock(SdmmcDriver *drv, uint32_t * pIoValClk)
+ {
+ uint32_t rc;
+
+ drv->control_param = *pIoValClk;
+
+ rc = sdmmc_device_control(drv, SDMMC_IOCTL_SET_CLOCK);
+
+ if (rc == SDMMC_OK || rc == SDMMC_CHANGED) {
+
+ *pIoValClk = drv->control_param;
+
+ TRACE_1("Device clk %lu kHz\n\r", drv->control_param / 1000UL);
+ }
+ return rc;
+ }
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.h
new file mode 100644
index 000000000..ebe0da07d
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_device.h
@@ -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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_macros.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_macros.h
new file mode 100644
index 000000000..52f82fd03
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_macros.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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.c
new file mode 100644
index 000000000..a6206854e
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.c
@@ -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
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.h
new file mode 100644
index 000000000..573c65059
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_mmc.h
@@ -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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.c
new file mode 100644
index 000000000..2f866296a
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.c
@@ -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
+}
+
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.h
new file mode 100644
index 000000000..2400ce842
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_pmc.h
@@ -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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sama5d2.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sama5d2.h
new file mode 100644
index 000000000..34a137b91
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sama5d2.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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.c
new file mode 100644
index 000000000..2f5e80a09
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.c
@@ -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));
+}
+
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.h
new file mode 100644
index 000000000..d08778219
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sd.h
@@ -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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.c
new file mode 100644
index 000000000..871788f0a
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.c
@@ -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
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.h
new file mode 100644
index 000000000..a2eba6649
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_sdio.h
@@ -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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.c b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.c
new file mode 100644
index 000000000..b8898acb5
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.c
@@ -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;
+}
+
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.h
new file mode 100644
index 000000000..1cce7cb85
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_tc.h
@@ -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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_trace.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_trace.h
new file mode 100644
index 000000000..1bb9ad15f
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ch_sdmmc_trace.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_ */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/driver.mk b/os/hal/ports/SAMA/LLD/SDMMCv1/driver.mk
new file mode 100644
index 000000000..ae2e9f16a
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/driver.mk
@@ -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
+
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/ffconf.h b/os/hal/ports/SAMA/LLD/SDMMCv1/ffconf.h
new file mode 100644
index 000000000..3937ff27e
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/ffconf.h
@@ -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 ---*/
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_conf.h b/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_conf.h
new file mode 100644
index 000000000..560fcb1c7
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_conf.h
@@ -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
+
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.c b/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.c
new file mode 100644
index 000000000..105e18036
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.c
@@ -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 */
+
+/** @} */
diff --git a/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.h b/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.h
new file mode 100644
index 000000000..9b672c642
--- /dev/null
+++ b/os/hal/ports/SAMA/LLD/SDMMCv1/sama_sdmmc_lld.h
@@ -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 */
+
+/** @} */