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 +#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 +#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 +#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 +#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 +#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 +#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 */ + +/** @} */