/* * Copyright (C) 2012 by Rob van Lieshout (info@pragmalab.nl) * Copyright (C) 2012 by Ole Reinhardt (ole.reinhardt@embedded-it.de) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ * * ************************************************************************** * * Parts taken from lpc177x_8x_mci.c 2011-06-02 * * file lpc177x_8x_mci.c * brief Contains all functions support for MCI firmware library * on LPC177x_8x * version 2.0 * date 29. June. 2011 * author NXP MCU SW Application Team * * Copyright(C) 2011, NXP Semiconductor * All rights reserved. * *********************************************************************** * Software that is described herein is for illustrative purposes only * which provides customers with programming information regarding the * products. This software is supplied "AS IS" without any warranties. * NXP Semiconductors assumes no responsibility or liability for the * use of the software, conveys no license or title under any patent, * copyright, or mask work right to the product. NXP Semiconductors * reserves the right to make changes in the software without * notification. NXP Semiconductors also make no representation or * warranty that such application will be suitable for the specified * use without further testing or modification. * Permission to use, copy, modify, and distribute this software and its * documentation is hereby granted, under NXP Semiconductors' * relevant copyright in the software, without fee, provided that it * is used in conjunction with NXP Semiconductors microcontrollers. This * copyright, permission, and disclaimer notice must appear in all copies of * this code. **********************************************************************/ #include #include #include #include #include #include //#define NUTDEBUG #ifdef NUTDEBUG #include #endif #define _SHIFT(x) (1 << x) #define _XSHIFT(x, y) (x << y) #define SHIFT_(x) (1 >> x) #define XSHIFT_(x, y) (x >> y) /* _SBF(f,v) sets the bit field starting at position "f" to value "v". * _SBF(f,v) is intended to be used in "OR" and "AND" expressions: * e.g., "((_SBF(5,7) | _SBF(12,0xF)) & 0xFFFF)" */ #define _SBF(f,v) (v<STATUS; /* handle MCI_STATUS interrupt */ if (MCI_Status & DATA_ERR_INT_MASK) { Lpc177x_8x_MciDataErrorProcess(); return; } if (MCI_Status & DATA_END_INT_MASK) { Lpc177x_8x_MciDATA_END_InterruptService(); return; } else if (MCI_Status & FIFO_INT_MASK) { Lpc177x_8x_MciFIFOInterruptService(); return; } else if (MCI_Status & CMD_INT_MASK) { Lpc177x_8x_MciCmdProcess(); return; } } /************************************************************************* * \brief Read data from FIFO (after a transmission with card) to * a destination buffer * * \param *dest The buffer to store the data that read from card * * \return MCI_FUNC_OK *************************************************************************/ static int32_t Lpc177x_8x_MciReadFifo(uint32_t * dest) { uint8_t i; uint8_t start, end; if (fifo_plane == 0) { start = 0; end = 7; } else { start = 8; end = 15; } fifo_plane = (fifo_plane) ? 0:1; for (i = start; i <= end; i++) { *dest = LPC_MCI->FIFO[i]; dest++; } return(MCI_FUNC_OK); } /*********************************************************************//** * \brief Write data from a source buffer to FIFO for transmission * * \param *src The buffer hold the data need to write to card * * \return MCI_FUNC_OK *************************************************************************/ static int32_t Lpc177x_8x_MciWriteFifo(uint32_t * src) { uint8_t i; uint8_t start, end; if (fifo_plane == 0) { start = 0; end = 7; } else { start = 8; end = 15; } fifo_plane = (fifo_plane) ? 0:1; for (i = start; i <= end; i++) { LPC_MCI->FIFO[i] = *src; src++; } return(MCI_FUNC_OK); } /*********************************************************************//** * \brief Enable Transmit data interrupt * * \param None * * \return None *************************************************************************/ void Lpc177x_8x_MciTXEnable( void ) { /* FIFO TX interrupts only */ LPC_MCI->MASK0 |= ((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); return; } /*********************************************************************//** * \brief Disable Transmit data interrupt * * \param None * * \return None *************************************************************************/ void Lpc177x_8x_MciTXDisable( void ) { /* FIFO TX interrupts only */ LPC_MCI->MASK0 &= ~((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); return; } /*********************************************************************//** * \brief Enable Receive data interrupt * * \param None * * \return None *************************************************************************/ void Lpc177x_8x_MciRXEnable( void ) { /* FIFO RX interrupts only */ LPC_MCI->MASK0 |= ((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); return; } /*********************************************************************//** * \brief Disable Receive data interrupt * * \param None * * \return None *************************************************************************/ void Lpc177x_8x_MciRXDisable( void ) { /* FIFO TX interrupts only */ LPC_MCI->MASK0 &= ~((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); return; } /*********************************************************************//** * \brief Check status when working with data-block transfer * * \details Right after the block read and write, this routine is important * that, even the FIFO is empty, complete block has been sent, but, * data is still being written to the card. This routine is to * ensure that the data has been written based on the state of * the card, not by the length being set. * * \param None * * \return MCI_FUNC_OK if all success *************************************************************************/ int32_t Lpc177x_8x_MciCheckStatus(void) { int32_t respValue, retval = MCI_FUNC_FAILED; /* * this code will keep reading the status of the card, untill the * card is READYFORDATA or the card is in TRAN state. * Only if the card command itself fails (so no repsonse could be * retrieved), this code will stop. */ while (1) { if (Lpc177x_8x_MciGetCardStatus(&respValue) != MCI_FUNC_OK) { #ifdef NUTDEBUG printf("%s() GetCardStatus failed\n", __FUNCTION__ ); #endif break; } else { /* * The only valid state is TRANS per MMC and SD state diagram. * RCV state may be seen, but, it happens only when TX_ACTIVE or * RX_ACTIVE occurs before the WRITE_BLOCK and READ_BLOCK cmds are * being sent, which is not a valid sequence. */ if (!(respValue & _SHIFT(MCI_CARDSTATUS_READYFORDATA_P0S))) { retval = MCI_FUNC_NOT_READY; #ifdef NUTDEBUG printf("%s() Card not ready\n", __FUNCTION__ ); #endif } else if (CARDSTATEOF(respValue) != MCI_CARDSTATE_TRAN) { /* Should be in STANDBY state now and ready */ retval = MCI_FUNC_ERR_STATE; #ifdef NUTDEBUG printf("%s() Invalid state\n", __FUNCTION__ ); #endif } else { return(MCI_FUNC_OK); } } } return(retval); } /*********************************************************************//** * \brief Called by MCI interrupt handler to simplify the command * process. * * \param None * * \return None * * \note In card initialization, the commnad interrupts are disabled *************************************************************************/ void Lpc177x_8x_MciCmdProcess( void ) { uint32_t MCIStatus; MCIStatus = LPC_MCI->STATUS; if (MCIStatus & MCI_CMD_CRC_FAIL) { LPC_MCI->CLEAR = MCI_CMD_CRC_FAIL; } if (MCIStatus & MCI_CMD_TIMEOUT) { LPC_MCI->CLEAR = MCI_CMD_TIMEOUT; } /* Cmd Resp End or Cmd Sent */ if (MCIStatus & MCI_CMD_RESP_END) { LPC_MCI->CLEAR = MCI_CMD_RESP_END; } if (MCIStatus & MCI_CMD_SENT) { LPC_MCI->CLEAR = MCI_CMD_SENT; } if (MCIStatus & MCI_CMD_ACTIVE) { LPC_MCI->CLEAR = MCI_CMD_ACTIVE; } return; } /*********************************************************************//** * \brief Called by MCI interrupt handler to manage error on the bus * * \param None * * \return None *************************************************************************/ void Lpc177x_8x_MciDataErrorProcess( void ) { uint32_t MCIStatus; MCIStatus = LPC_MCI->STATUS; if (MCIStatus & MCI_DATA_CRC_FAIL) { LPC_MCI->CLEAR = MCI_DATA_CRC_FAIL; } if (MCIStatus & MCI_DATA_TIMEOUT) { LPC_MCI->CLEAR = MCI_DATA_TIMEOUT; } /* Underrun or overrun */ if (MCIStatus & MCI_TX_UNDERRUN) { LPC_MCI->CLEAR = MCI_TX_UNDERRUN; } if (MCIStatus & MCI_RX_OVERRUN) { LPC_MCI->CLEAR = MCI_RX_OVERRUN; } /* Start bit error on data signal */ if (MCIStatus & MCI_START_BIT_ERR) { LPC_MCI->CLEAR = MCI_START_BIT_ERR; } Mci_Data_Xfer_End = 0; Mci_Data_Xfer_ERR = MCIStatus; return; } /*********************************************************************//** * \brief Called by MCI interrupt handler. This is the last interrupt * manipulates the process of the data-block write and read * to/with card * * \details This service is also used with/without DMA support. It simply * clears the flag messages the in-process data-block transfer * has been done/ended * * \param None * * \return None *************************************************************************/ void Lpc177x_8x_MciDATA_END_InterruptService( void ) { uint32_t MCIStatus; MCIStatus = LPC_MCI->STATUS; if (MCIStatus & MCI_DATA_END) /* Data end, and Data block end */ { LPC_MCI->CLEAR = MCI_DATA_END; Mci_Data_Xfer_End = 0; Mci_Data_Xfer_ERR = 0; Lpc177x_8x_MciTXDisable(); Lpc177x_8x_MciRXDisable(); return; } if (MCIStatus & MCI_DATA_BLK_END) { LPC_MCI->CLEAR = MCI_DATA_BLK_END; return; } /* Tx active */ if (MCIStatus & MCI_TX_ACTIVE) { } /* Rx active */ if (MCIStatus & MCI_RX_ACTIVE) { } return; } /*********************************************************************//** * \brief Called by MCI interrupt handler if requiring to using FIFO * for data transferring. It copy data to/from FIFO register * from/to a data buffer. * * \param None * * \return None * * \note This function is done without DMA transfer support **********************************************************************/ void Lpc177x_8x_MciFIFOInterruptService( void ) { uint32_t MCIStatus; MCIStatus = LPC_MCI->STATUS; if (MCIStatus & (FIFO_TX_INT_MASK )) { // empty is multiple of 512 block size if (MCIStatus & MCI_TX_HALF_EMPTY) { // There's no data, return if (dataSrcBlock == NULL) { return; } // write 8 words to fifo Lpc177x_8x_MciWriteFifo((uint32_t *)&dataSrcBlock[txBlockCnt]); txBlockCnt += 32; // LPC_MCI->CLEAR = MCI_TX_HALF_EMPTY; // no need to clear the pending interrupt here? } if (txBlockCnt == BLOCK_LENGTH) /* block complete */ { dataSrcBlock += BLOCK_LENGTH; txBlockCnt = 0; // now wait for SD card to complete sending data i.e MCI_DATA_BLK_END interrupt } } else if (MCIStatus & (FIFO_RX_INT_MASK)) { // if using RX_HALF_FULL remove one ReadFIFO below if (MCIStatus & MCI_RX_HALF_FULL) { if (dataDestBlock == NULL) { return; } // read 8 words from fifo Lpc177x_8x_MciReadFifo((uint32_t *)&dataDestBlock[rxBlockCnt]); rxBlockCnt += 32; // LPC_MCI->CLEAR = MCI_RX_HALF_FULL; // no need to clear the pending interrupt here? } // block complete if (rxBlockCnt == BLOCK_LENGTH) { dataDestBlock += BLOCK_LENGTH; rxBlockCnt = 0; // now wait for SD card to complete sending data i.e MCI_DATA_BLK_END interrupt } } return; } /*********************************************************************//** * \brief Set MCI clock rate, during initialization phase < 400K * during data phase < 20Mhz * * \param[in] ClockRate Clock rate to be set * * \return None **********************************************************************/ void Lpc177x_8x_MciSetClock( uint32_t ClockRate ) { uint32_t ClkValue = 0; #ifdef USE_CLOCK_SETTINGS uint32_t PeriClock; /* * ClockRate is fixed in this code (#define in headerfile), but better would be to get * the peripheral clock and see what the maximum speed could be. * However, tests with the IAR LPC1788 dev-board show that the maximum clock rate MCI_NORMAL * is only slighty higher then the clock rate that goes with MCI_SLOW_RATE. * So no use to calculate the clockrate here.... */ PeriClock = Lpc17xx_ClockGet(NUT_HWCLK_PERIPHERAL); #endif if (ClockRate == MCI_SLOW_RATE) { /* slow clock */ ClkValue |= MCLKDIV_SLOW; } else if (ClockRate == MCI_NORMAL_RATE) { /* normal clock */ ClkValue |= MCLKDIV_NORMAL; } LPC_MCI->CLOCK &= ~(0xFF); /* clear clock divider */ LPC_MCI->CLOCK |= (1 << 8) | ClkValue; NutMicroDelay(1); // delay 3MCLK + 2PCLK before next write return; } /**********************************************************************//** * \brief Set the Width to 1-bit Bus or 4-bit Bus * * \param[in] width buswidth expected to set * * \return MCI_FUNC_OK in case of success *************************************************************************/ int32_t Lpc177x_8x_MciSetBusWidth( uint32_t width ) { NutMicroDelay(1); /* delay 3MCLK + 2PCLK */ if (width == SD_1_BIT) { LPC_MCI->CLOCK &= ~(1 << 11); /* 1 bit bus */ } else if (width == SD_4_BIT) { LPC_MCI->CLOCK |= (1 << 11);/* 4 bit bus */ } if (Lpc177x_8x_MciAcmd_SendBusWidth( BUS_WIDTH_4BITS ) != MCI_FUNC_OK) { return(MCI_FUNC_FAILED); } return(MCI_FUNC_OK); } /************************************************************************//** * \brief Do initialization the MCI block as set its clock, registers, * setup NVIC for interrupts, configure the pins used for MCI * function, do initialize the card in slot... * * \param[in] powerActiveLevel the power level to activate the card in slot * * \return MCI_FUNC_OK in case of success ***************************************************************************/ int32_t Lpc177x_8x_MciInit(uint8_t powerActiveLevel ) { MCI_CardType = MCI_CARD_UNKNOWN; #if 0 /******* GPIO Pin initialisation has to be done in the users program prior to device initialisation ********/ /* * Following block of code added to ensure card VCC drops to zero before card is initialized * * PragmaLab 15-08-2012: -> with next lines of code enabled, it is not possible to execute the Init more then * one time in a row. We need 2 times (RegisterDevive and _open both call this Init routine) * */ // Force all MCI control pins to basic I/O mode GpioPinConfigSet(NUTGPIO_PORT0, 19, GPIO_CFG_OUTPUT); // SD_CLK GpioPinConfigSet(NUTGPIO_PORT0, 20, GPIO_CFG_OUTPUT); // SD_CMD GpioPinConfigSet(NUTGPIO_PORT1, 5, GPIO_CFG_OUTPUT); // SD_PWR GpioPinConfigSet(NUTGPIO_PORT0, 22, GPIO_CFG_OUTPUT); // SD_DAT_0 GpioPinConfigSet(NUTGPIO_PORT1, 7, GPIO_CFG_OUTPUT); // SD_DAT_1 GpioPinConfigSet(NUTGPIO_PORT1, 11, GPIO_CFG_OUTPUT); // SD_DAT_2 GpioPinConfigSet(NUTGPIO_PORT1, 12, GPIO_CFG_OUTPUT); // SD_DAT_3 // Force all pins low (except power control pin) GpioPinSetLow(NUTGPIO_PORT0, 19); // CLK GpioPinSetLow(NUTGPIO_PORT0, 20); // CMD GpioPinSetHigh(NUTGPIO_PORT1, 5); // PWR GpioPinSetLow(NUTGPIO_PORT0, 22); // DAT0 GpioPinSetLow(NUTGPIO_PORT1, 7); // DAT1 GpioPinSetLow(NUTGPIO_PORT1, 11); // DAT2 GpioPinSetLow(NUTGPIO_PORT1, 12); // DAT3 // Crude delay of 50ms NutSleep(50); #endif LPC_SC->PCONP |= ( 1 << 28 ); // Enable clock to the MCI block if (LPC_MCI->CLOCK & (1 << 8)) { LPC_MCI->CLOCK &= ~(1 << 8); // disable clock for now } if (LPC_MCI->POWER & 0x02) { LPC_MCI->POWER = 0x00; } NutDelay(1); // Disable all interrupts for now LPC_MCI->MASK0 = 0; /* * now re-configure all pins to serve as SDIO * * PragmaLab 18-09-2012: make sure you configure the SDIO pins only once at startup, no need * to re-configure them here * GpioPinConfigSet(NUTGPIO_PORT0, 19, GPIO_CFG_PERIPHERAL2); // SD_CLK GpioPinConfigSet(NUTGPIO_PORT0, 20, GPIO_CFG_PERIPHERAL2); // SD_CMD GpioPinConfigSet(NUTGPIO_PORT1, 5, GPIO_CFG_PERIPHERAL2); // SD_PWR GpioPinConfigSet(NUTGPIO_PORT0, 22, GPIO_CFG_PERIPHERAL2); // SD_DAT_0 GpioPinConfigSet(NUTGPIO_PORT1, 7, GPIO_CFG_PERIPHERAL2); // SD_DAT_1 GpioPinConfigSet(NUTGPIO_PORT1, 11, GPIO_CFG_PERIPHERAL2); // SD_DAT_2 GpioPinConfigSet(NUTGPIO_PORT1, 12, GPIO_CFG_PERIPHERAL2); // SD_DAT_3 */ // SD_PWR is active high (follows the output of the SD Card interface block). if (powerActiveLevel == LOW_LVL) { LPC_SC->SCS &= ~ 0x08; } else { LPC_SC->SCS |= 0x08; } // Setting for timeout problems LPC_MCI->DATATMR = 0x1FFFFFFF; // set up clocking default mode, clear any registers as needed LPC_MCI->COMMAND = 0; LPC_MCI->DATACTRL = 0; LPC_MCI->CLEAR = 0x7FF; // clear all pending interrupts LPC_MCI->POWER = 0x02; // power up while (!(LPC_MCI->POWER & 0x02)); // wait till power is there.... NutMicroDelay(10); /* * During identification phase, the clock should be less than * 400Khz. Once we pass this phase, the normal clock can be set up * to 25Mhz on SD card and 20Mhz on MMC card. */ Lpc177x_8x_MciSetClock(MCI_SLOW_RATE ); LPC_MCI->POWER |= 0x01; /* bit 1 is set already, from power up to power on */ NutMicroDelay(200); NutRegisterIrqHandler(&sig_MCI, Lpc177x_8x_MciIRQHandler, 0); NutIrqEnable (&sig_MCI); Lpc177x_8x_MciIrqSetPriority(SD_INTERRUPT_PRIORITY); #ifdef NUTDEBUG printf("SD int priority: %d\n", Lpc177x_8x_MciIrqGetPriority()); #endif /* During the initialization phase, to simplify the process, the CMD related * interrupts are disabled. The DATA related interrupts are enabled when * the FIFOs are used and just before WRITE_BLOCK READ_BLOCK cmds are issues, and * disabled after the data block has been written and read. Please also note, * before WRITE_BLOCK only TX related data interrupts are enabled, and before * READ_BLOCK only RX related data interrupts are enabled. */ return(Lpc177x_8x_MciCardInit()); } /************************************************************************//** * \brief Set output in open drain mode or pushpull mode * * \param[in] mode the mode going to set * * \return None ***************************************************************************/ void Lpc177x_8x_MciSetOutputMode(uint32_t mode) { if (mode == MCI_OUTPUT_MODE_OPENDRAIN) { /* Set Open Drain output control for MMC */ LPC_MCI->POWER |= (1 << MCI_PWRCTRL_OPENDRAIN_POS) & MCI_PWRCTRL_BMASK; } else { /* Clear Open Drain output control for SD */ LPC_MCI->POWER &= (~(1 << MCI_PWRCTRL_OPENDRAIN_POS) & MCI_PWRCTRL_BMASK); } } /************************************************************************//** * \brief The routine is used to send a CMD to the card * * \param[in] CmdIndex the command to be sent to cards * * \param[in] Argument the argument follows the command * * \param[in] ExpectResp the response type for the command. They may be: * - EXPECT_NO_RESP: means no response required * - EXPECT_SHORT_RESP: means a response in a word needed * - EXPECT_LONG_RESP: means a response in 4 words needed * * \param[in] AllowTimeout allow timeout the command or not * * \return None ***************************************************************************/ void Lpc177x_8x_MciSendCmd(uint32_t CmdIndex, uint32_t Argument, uint32_t ExpectResp, uint32_t AllowTimeout) { uint32_t CmdData = 0; uint32_t CmdStatus; /* * the command engine must be disabled when we modify the argument * or the peripheral resends */ while ((CmdStatus = LPC_MCI->STATUS) & MCI_CMD_ACTIVE) /* Command in progress. */ { LPC_MCI->COMMAND = 0; LPC_MCI->CLEAR = CmdStatus | MCI_CMD_ACTIVE; } NutMicroDelay(10); // set the command details, the CmdIndex should 0 through 0x3F only CmdData |= (CmdIndex & 0x3F); /* bit 0 through 5 only */ if (ExpectResp == EXPECT_NO_RESP) /* no response */ { CmdData &= ~((1 << 6) | (1 << 7)); /* Clear long response bit as well */ } else if (ExpectResp == EXPECT_SHORT_RESP) /* expect short response */ { CmdData |= (1 << 6); } else if (ExpectResp == EXPECT_LONG_RESP) /* expect long response */ { CmdData |= (1 << 6) | (1 << 7); } if (AllowTimeout == ALLOW_CMD_TIMER) /* allow timeout or not */ { CmdData &= ~ MCI_DISABLE_CMD_TIMER; } else { CmdData |= MCI_DISABLE_CMD_TIMER; } /* send the command */ CmdData |= (1 << 10); /* This bit needs to be set last. */ LPC_MCI->ARGUMENT = Argument; /* Set the argument first, finally command */ LPC_MCI->COMMAND = CmdData; return; } /************************************************************************//** * \brief The routine is to get the reponse from card after commands. * This function is always used in pair of Lpc177x_8x_MciSendCmd() func * * \param[in] ExpectCmdData specify the command of which the data will be * retrieved. This field should be the same with CmdIndex of * Lpc177x_8x_MciSendCmd() function. * * \param[in] ExpectResp the response type for the command. They may be: * - EXPECT_NO_RESP: means no response required * - EXPECT_SHORT_RESP: means a response in a word needed * - EXPECT_LONG_RESP: means a response in 4 words needed * * \param[out] CmdResp the buffer stored the data replied from cards * * \return MCI_FUNC_OK in case of success ***************************************************************************/ int32_t Lpc177x_8x_MciGetCmdResp(uint32_t ExpectCmdData, uint32_t ExpectResp, uint32_t *CmdResp) { uint32_t CmdRespStatus = 0; uint32_t LastCmdIndex; if (ExpectResp == EXPECT_NO_RESP) { return(MCI_FUNC_OK); } while (1) { // Get the status of the component CmdRespStatus = LPC_MCI->STATUS; if (CmdRespStatus & (MCI_CMD_TIMEOUT)) { LPC_MCI->CLEAR = CmdRespStatus | MCI_CMD_TIMEOUT; LPC_MCI->COMMAND = 0; LPC_MCI->ARGUMENT = 0xFFFFFFFF; return(CmdRespStatus); } if (CmdRespStatus & MCI_CMD_CRC_FAIL) { LPC_MCI->CLEAR = CmdRespStatus | MCI_CMD_CRC_FAIL; LastCmdIndex = LPC_MCI->COMMAND & 0x003F; if ((LastCmdIndex == CMD1_SEND_OP_COND) || (LastCmdIndex == ACMD41_SEND_APP_OP_COND) || (LastCmdIndex == CMD12_STOP_TRANSMISSION) ) { LPC_MCI->COMMAND = 0; LPC_MCI->ARGUMENT = 0xFFFFFFFF; // ignore CRC error if it's a resp for SEND_OP_COND or STOP_TRANSMISSION. break; } else { return(CmdRespStatus); } } else if (CmdRespStatus & MCI_CMD_RESP_END) { LPC_MCI->CLEAR = CmdRespStatus | MCI_CMD_RESP_END; break; // cmd response is received, expecting response } } if ((LPC_MCI->RESP_CMD & 0x3F) != ExpectCmdData) { /* * If the response is not R1, in the response field, the Expected Cmd data * won't be the same as the CMD data in SendCmd(). Below four cmds have * R2 or R3 response. We don't need to check if MCI_RESP_CMD is the same * as the Expected or not. */ if ((ExpectCmdData != CMD1_SEND_OP_COND) && (ExpectCmdData != ACMD41_SEND_APP_OP_COND) && (ExpectCmdData != CMD2_ALL_SEND_CID) && (ExpectCmdData != CMD9_SEND_CSD) ) { CmdRespStatus = INVALID_RESPONSE; /* Reuse error status */ return(INVALID_RESPONSE); } } /* Read MCI_RESP0 register assuming it's not long response. */ if (CmdResp != NULL) { if (ExpectResp == EXPECT_SHORT_RESP) { *(CmdResp + 0) = LPC_MCI->RESP0; *(CmdResp + 1) = 0; *(CmdResp + 2) = 0; *(CmdResp + 3) = 0; } else if (ExpectResp == EXPECT_LONG_RESP) { *(CmdResp + 0) = LPC_MCI->RESP0; *(CmdResp + 1) = LPC_MCI->RESP1; *(CmdResp + 2) = LPC_MCI->RESP2; *(CmdResp + 3) = LPC_MCI->RESP3; } } return(MCI_FUNC_OK); } /************************************************************************//** * \brief The routine is to send command to cards then get back the * reponses (if required). * * \param[in] CmdIndex the command to be sent to cards * * \param[in] Argument the argument follows the command * * \param[in] ExpectResp the response type for the command. They may be: * - EXPECT_NO_RESP: means no response required * - EXPECT_SHORT_RESP: means a response in a word needed * - EXPECT_LONG_RESP: means a response in 4 words needed * * \param[out] CmdResp the buffer stored the data replied from cards * * \param[in] AllowTimeout allow timeout the command or not * * \return MCI_FUNC_OK in case of success ***************************************************************************/ int32_t Lpc177x_8x_MciCmdResp(uint32_t CmdIndex, uint32_t Argument, uint32_t ExpectResp, uint32_t *CmdResp, uint32_t AllowTimeout) { int32_t respStatus; Lpc177x_8x_MciSendCmd(CmdIndex, Argument, ExpectResp, AllowTimeout); if ((CmdResp != NULL) || (ExpectResp != EXPECT_NO_RESP)) { respStatus = Lpc177x_8x_MciGetCmdResp(CmdIndex, ExpectResp, CmdResp); } else { respStatus = MCI_FUNC_BAD_PARAMETERS; } #ifdef NUTDEBUG if(respStatus & (MCI_CMD_TIMEOUT)) { printf("%s() failed with MCI_CMD_TIMEOUT\n", __FUNCTION__ ); } if(respStatus & (MCI_CMD_CRC_FAIL)) { printf("%s() failed with MCI_CMD_CRC_FAIL\n", __FUNCTION__ ); } if(respStatus == INVALID_RESPONSE) { printf("%s() failed with INVALID_RESPONSE\n", __FUNCTION__ ); } if(respStatus == MCI_FUNC_BAD_PARAMETERS) { printf("%s() failed with MCI_FUNC_BAD_PARAMETERS\n", __FUNCTION__ ); } #endif return(respStatus); } /************************************************************************//** * \brief To reset the card, the CMD0 is sent and then the card is put * in idle state. This is the very first command to be sent to * initialize either MMC or SD card. * * \param None * * \return Always MCI_FUNC_OK ***************************************************************************/ int32_t Lpc177x_8x_MciCardReset(void) { /* * Because CMD0 command to put the device to idle state does not need response * since, it's only sending commad */ Lpc177x_8x_MciSendCmd(CMD0_GO_IDLE_STATE, 0x00000000, EXPECT_NO_RESP, 0); return(MCI_FUNC_OK); } /************************************************************************//** * \brief Send CMD1 (SEND_OP_COND) to card. * * \param None * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciCmd_SendOpCond( void ) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; int32_t retval = MCI_FUNC_FAILED; retryCount = 0x200; /* reset retry counter */ while (retryCount > 0) { respStatus = Lpc177x_8x_MciCmdResp(CMD1_SEND_OP_COND, OCR_INDEX, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus & MCI_CMD_TIMEOUT) { retval = MCI_FUNC_TIMEOUT; } else if ((respValue[0] & 0x80000000) == 0) { // the card has not finished the power up routine retval = MCI_FUNC_BUS_NOT_IDLE; } else { retval = MCI_FUNC_OK; break; } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_TIMEOUT) { printf("%s() failed with MCI_FUNC_TIMEOUT\n", __FUNCTION__ ); } if(retval == MCI_FUNC_BUS_NOT_IDLE) { printf("%s() failed with MCI_FUNC_BUS_NOT_IDLE\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Send CMD8 (SEND_IF_COND) for interface condition to card. * * \param None * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciCmd_SendIfCond(void) { uint32_t retryCount; uint32_t CmdArgument; uint32_t respStatus; uint32_t respValue[4]; int32_t retval = MCI_FUNC_FAILED; uint8_t voltageSupplied = 0x01; // in range 2.7-3.6V uint8_t checkPattern = 0xAA; CmdArgument = (voltageSupplied << MCI_CMD8_VOLTAGESUPPLIED_POS) | checkPattern; retryCount = 0x20; while (retryCount > 0) { respStatus = Lpc177x_8x_MciCmdResp(CMD8_SEND_IF_COND, CmdArgument, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus & (MCI_CMD_TIMEOUT)) { // consider as no response retval = MCI_FUNC_TIMEOUT; } else if ((respValue[0] & MCI_CMD8_CHECKPATTERN_BMASK) != checkPattern) { retval = MCI_FUNC_FAILED; } else if (((respValue[0] >> MCI_CMD8_VOLTAGESUPPLIED_POS) & MCI_CMD8_VOLTAGESUPPLIED_BMASK) != voltageSupplied) { retval = MCI_FUNC_BAD_PARAMETERS; } else { retval = MCI_FUNC_OK; break; } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_TIMEOUT) { printf("%s() failed with MCI_FUNC_TIMEOUT\n", __FUNCTION__ ); } if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_BAD_PARAMETERS) { printf("%s() failed with MCI_FUNC_BAD_PARAMETERS\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Send CMD55 (APP_CMD) to indicate to the card that the next * command is an application specific command rather than a * standard command. Before an ACMD, call this routine first * * \param None * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciCmd_SendACMD( void ) { uint32_t retryCount; uint32_t CmdArgument; uint32_t respStatus; uint32_t respValue[4]; int32_t retval = MCI_FUNC_FAILED; if ((MCI_CardType == MCI_SDSC_V1_CARD) || (MCI_CardType == MCI_SDSC_V2_CARD) || (MCI_CardType == MCI_SDHC_SDXC_CARD)) { CmdArgument = CardRCA; /* Use the address from SET_RELATIVE_ADDR cmd */ } else /* if MMC or unknown card type, use 0x0. */ { CmdArgument = 0x00000000; } retryCount = 0x20; while (retryCount > 0) { respStatus = Lpc177x_8x_MciCmdResp(CMD55_APP_CMD, CmdArgument, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus & (MCI_CMD_TIMEOUT)) // filter out TIMEOUT error to be able to quickly respond missing cards { retval = MCI_FUNC_TIMEOUT; } else if (respStatus != 0) // not one of MCI_ errors coming from the status register { retval = MCI_FUNC_FAILED; } else if (respValue[0] & CARD_STATUS_ACMD_ENABLE) { retval = MCI_FUNC_OK; break; } else { retval = MCI_FUNC_NOT_READY; } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_TIMEOUT) { printf("%s() failed with MCI_FUNC_TIMEOUT\n", __FUNCTION__ ); } if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Send ACMD41 (SEND_APP_OP_COND) to Host Capacity Support (HCS) * information and asks the accessed card to send its operating * condition (OCR). * * \param[in] hcsVal input the Host Capacity Support * * \return MCI_FUNC_OK if all success * * \note If SEND_APP_OP_COND is timeout, the card in the slot is not MMC * type, try this combination to see if we can communicate with * a SD type. ****************************************************************************/ int32_t Lpc177x_8x_MciAcmd_SendOpCond(uint8_t hcsVal) { uint32_t retryCount; uint32_t respStatus, argument; uint32_t respValue[4]; int32_t retval = MCI_FUNC_FAILED; argument = OCR_INDEX | (hcsVal << MCI_ACMD41_HCS_POS); /* * timeout on SEND_OP_COND command on MMC, now, try SEND_APP_OP_COND * command to SD */ retryCount = 0x200; /* reset retry counter */ while (retryCount > 0) { if ((retval = Lpc177x_8x_MciCmd_SendACMD()) == MCI_FUNC_OK) { respStatus = Lpc177x_8x_MciCmdResp(ACMD41_SEND_APP_OP_COND, argument, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus & (MCI_CMD_TIMEOUT)) { retval = MCI_FUNC_TIMEOUT; } else if (!(respValue[0] & 0x80000000)) { retval = MCI_FUNC_BUS_NOT_IDLE; } else { CCS = (respValue[0] & (1<<30)) ? 1:0; retval = MCI_FUNC_OK; break; } } else { /* * check for TIMEOUT errors here and treat them in a special way. In case no card is present, we * want to quit the interrogration asap. */ if (retval & (MCI_CMD_TIMEOUT)) { retval = MCI_FUNC_TIMEOUT; break; } } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_CMD_TIMEOUT) { printf("%s() failed with MCI_CMD_TIMEOUT\n", __FUNCTION__ ); } if(retval == MCI_FUNC_BUS_NOT_IDLE) { printf("%s() failed with MCI_FUNC_BUS_NOT_IDLE\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Do initialization for the card in the slot * * \details Try CMD1 first for MMC, if it's timeout, try CMD55 * and CMD41 for SD, if both failed, initialization faliure, * bailout with unknown card type. Otherwise, return the * card type, either MMC or SD. <<>> * * This is followed Figure 4-2: Card Initialization and * Identification Flow (SD mode) in Physical Layer Simplified * Specification Version 2.00 document * * \param None * * \return MCI_FUNC_OK if success ****************************************************************************/ int32_t Lpc177x_8x_MciCardInit( void ) { int32_t retval = MCI_FUNC_FAILED; MCI_CardType = MCI_CARD_UNKNOWN; // expect no response, so no returnvalue to check... Lpc177x_8x_MciCardReset(); /* Clear Open Drain output control for SD */ Lpc177x_8x_MciSetOutputMode(MCI_OUTPUT_MODE_PUSHPULL); NutMicroDelay(300); retval = Lpc177x_8x_MciCmd_SendIfCond(); if (retval == MCI_FUNC_BAD_PARAMETERS) { // Unknow card is unusable return(retval); } /* * please note that MCI_FUNC_TIMEOUT error is allowed here */ if (retval == MCI_FUNC_OK) /* Ver2.00 or later*/ { // Check in case of High Capacity Supporting Host if ((retval = Lpc177x_8x_MciAcmd_SendOpCond(1)) == MCI_FUNC_OK) { MCI_CardType = MCI_SDSC_V2_CARD;//SDSC if (CCS) { MCI_CardType = MCI_SDHC_SDXC_CARD;//SDHC or SDXC } return(MCI_FUNC_OK); /* Found the card, it's a HD */ } } if (retval != MCI_FUNC_OK) /* voltage mismatch (ver2.00) or ver1.X SD Card or not SD Card*/ { // Check in case of Standard Capacity Supporting Host if ((retval = Lpc177x_8x_MciAcmd_SendOpCond(0)) == MCI_FUNC_OK) { MCI_CardType = MCI_SDSC_V1_CARD;// Support Standard Capacity only return(MCI_FUNC_OK); /* Found the card, it's a SD */ } } if (retval != MCI_FUNC_OK) { /* Set Open Drain output control for MMC */ Lpc177x_8x_MciSetOutputMode(MCI_OUTPUT_MODE_OPENDRAIN); NutMicroDelay(300); /* * Try CMD1 first for MMC, if it's timeout, try CMD55 and CMD41 for SD, * if both failed, initialization faIlure, bailout. */ if (Lpc177x_8x_MciCmd_SendOpCond() == MCI_FUNC_OK) { MCI_CardType = MCI_MMC_CARD; return(MCI_FUNC_OK); /* Found the card, it's a MMC */ } } #ifdef NUTDEBUG printf("%s()failed\n", __FUNCTION__ ); #endif /* tried both MMC and SD card, give up */ return(MCI_FUNC_FAILED); } /************************************************************************//** * \brief Get the type of card that is currently used in the slot * * \param None * * \return Card Type: MMC Card or SD card ****************************************************************************/ en_Mci_CardType Lpc177x_8x_MciGetCardType(void) { return(MCI_CardType); } /************************************************************************//** * \brief Get the all the Identifier (CID) of the card by sending the * CMD2 (ALL_SEND_CID) command. Then parse 4-byte data obtained * from the card by the CID meaning. * * \param[out] cidValue the CID Result after parsing the data from the card * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciGetCID(st_Mci_CardId* cidValue) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; /* This command is normally after CMD1(MMC) or ACMD41(SD). */ retryCount = 0x20; /* reset retry counter */ while (retryCount > 0) { respStatus = Lpc177x_8x_MciCmdResp(CMD2_ALL_SEND_CID, 0, EXPECT_LONG_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); /* bit 0 and bit 2 must be zero, or it's timeout or CRC error */ //if ((!(respStatus & MCI_CMD_TIMEOUT)) && (!(respStatus & MCI_CMD_CRC_FAIL))) if (!(respStatus & MCI_CMD_TIMEOUT)) { // Parsing the data retrieved if (cidValue != NULL) { cidValue->MID = (respValue[0] >> MCI_CID_MANUFACTURER_ID_WPOS) & MCI_CID_MANUFACTURER_ID_WBMASK; cidValue->OID = (respValue[0] >> MCI_CID_OEMAPPLICATION_ID_WPOS) & MCI_CID_OEMAPPLICATION_ID_WBMASK; cidValue->PNM_H = (respValue[0] >> MCI_CID_PRODUCTNAME_ID_H_WPOS) & MCI_CID_PRODUCTNAME_ID_H_WBMASK; cidValue->PNM_L = (respValue[1] >> MCI_CID_PRODUCTNAME_ID_L_WPOS) & MCI_CID_PRODUCTNAME_ID_L_WBMASK; cidValue->PRV = (respValue[2] >> MCI_CID_PRODUCTREVISION_ID_WPOS) & MCI_CID_PRODUCTREVISION_ID_WBMASK; cidValue->PSN = (((respValue[2] >> MCI_CID_PRODUCTSERIALNUM_ID_H_WPOS) & MCI_CID_PRODUCTSERIALNUM_ID_H_WBMASK) << 8) | ((respValue[3] >> MCI_CID_PRODUCTSERIALNUM_ID_L_WPOS) & MCI_CID_PRODUCTSERIALNUM_ID_L_WBMASK); cidValue->reserved = (respValue[3] >> MCI_CID_RESERVED_ID_WPOS) & MCI_CID_RESERVED_ID_WBMASK; cidValue->MDT = (respValue[3] >> MCI_CID_MANUFACTURINGDATE_ID_WPOS) & MCI_CID_MANUFACTURINGDATE_ID_WBMASK; cidValue->CRC = (respValue[3] >> MCI_CID_CHECKSUM_ID_WPOS) & MCI_CID_CHECKSUM_ID_WBMASK; cidValue->unused = (respValue[3] >> MCI_CID_UNUSED_ID_WPOS) & MCI_CID_UNUSED_ID_WBMASK; } return(MCI_FUNC_OK); /* response is back and correct. */ } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG printf("%s() failed\n", __FUNCTION__ ); #endif return(MCI_FUNC_TIMEOUT); } /************************************************************************//** * \brief Set the address for the card in the slot by sending CMD3 * (SET_RELATIVE_ADDR) command. To get the address of the card * currently in used, needs to call Lpc177x_8x_MciGetCardAddress() * * \param None * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciSetCardAddress( void ) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; uint32_t CmdArgument; int32_t retval = MCI_FUNC_FAILED; /* * If it's a SD card, SET_RELATIVE_ADDR is to get the address * from the card and use this value in RCA, if it's a MMC, set default * RCA addr. 0x00010000. */ if ((MCI_CardType == MCI_SDSC_V1_CARD) || (MCI_CardType == MCI_SDSC_V2_CARD) || (MCI_CardType == MCI_SDHC_SDXC_CARD)) { CmdArgument = 0; } else /* If it's unknown or MMC_CARD, fix the RCA address */ { CmdArgument = 0x00010000; } retryCount = 0x20; /* reset retry counter */ while (retryCount > 0) { /* Send CMD3 command repeatedly until the response is back correctly */ respStatus = Lpc177x_8x_MciCmdResp(CMD3_SET_RELATIVE_ADDR, CmdArgument, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus & (MCI_CMD_TIMEOUT)) { retval = MCI_FUNC_TIMEOUT; } else if (!(XSHIFT_(respValue[0], MCI_CARDSTATUS_READYFORDATA_P0S) & 0x01)) { retval = MCI_FUNC_NOT_READY; } else if ((CARDSTATEOF(respValue[0]) != MCI_CARDSTATE_IDENDTIFIED)) { retval = MCI_FUNC_ERR_STATE; } else { CardRCA = respValue[0] & 0xFFFF0000; /* Save the RCA value from SD card */ return(MCI_FUNC_OK); /* response is back and correct. */ } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_TIMEOUT) { printf("%s() failed with MCI_FUNC_TIMEOUT\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } if(retval == MCI_FUNC_ERR_STATE) { printf("%s() failed with MCI_FUNC_ERR_STATE\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Get the address for the card in the slot * * \param None * * \return MCI_FUNC_OK if all success * * \note This function must be called after Lpc177x_8x_MciSetCardAddress() executing ****************************************************************************/ uint32_t Lpc177x_8x_MciGetCardAddress(void) { return(CardRCA); } /************************************************************************//** * \brief Get the Card-Specific Data of in-slot card by sending CMD9 * (SEND_CSD) command * * \param[out] csdVal a buffer stored the value of CSD obtained from the card * * \return MCI_FUNC_OK if all success * * \note CMD9 (SEND_CSD) command should be sent only at standby state * (STBY) after CMD3 ****************************************************************************/ int32_t Lpc177x_8x_MciGetCSD(uint32_t* csdVal) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; uint32_t CmdArgument; if ((MCI_CardType == MCI_SDSC_V1_CARD) || (MCI_CardType == MCI_SDSC_V2_CARD) || (MCI_CardType == MCI_SDHC_SDXC_CARD)) { CmdArgument = CardRCA; } else /* if MMC or unknown card type, use default RCA addr. */ { CmdArgument = 0x00010000; } retryCount = 0x20; while (retryCount > 0) { /* Send SET_BLOCK_LEN command before read and write */ LPC_MCI->CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END); respStatus = Lpc177x_8x_MciCmdResp(CMD9_SEND_CSD, CmdArgument, EXPECT_LONG_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (!respStatus) { if (csdVal != NULL) { csdVal[0] = respValue[0]; csdVal[1] = respValue[1]; csdVal[2] = respValue[2]; csdVal[3] = respValue[3]; } return(MCI_FUNC_OK); } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG printf("%s() failed\n", __FUNCTION__ ); #endif return(MCI_FUNC_FAILED); } /************************************************************************//** * \brief Select the card by the specified address. This is done by sending * out the CMD7 with the address argument to needed card * * \param None * * \return MCI_FUNC_OK if all success * * \note CMD7 (SELECT_CARD) command should be sent after CMD9 ((SEND_CSD). * The state will be inter-changed between STBY to TRANS by this * CMD7 command ****************************************************************************/ int32_t Lpc177x_8x_MciCmd_SelectCard( void ) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; uint32_t CmdArgument; int32_t retval = MCI_FUNC_FAILED; if ((MCI_CardType == MCI_SDSC_V1_CARD) || (MCI_CardType == MCI_SDSC_V2_CARD) || (MCI_CardType == MCI_SDHC_SDXC_CARD)) { CmdArgument = CardRCA; } else /* if MMC or unknown card type, use default RCA addr. */ { CmdArgument = 0x00010000; } retryCount = 0x20; while (retryCount > 0) { /* Send CMD7_SELECT_CARD command before read and write */ LPC_MCI->CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END); respStatus = Lpc177x_8x_MciCmdResp(CMD7_SELECT_CARD, CmdArgument, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus) { retval = MCI_FUNC_FAILED; } else if (!(respValue[0] & _SHIFT(MCI_CARDSTATUS_READYFORDATA_P0S))) { retval = MCI_FUNC_NOT_READY; } else if (CARDSTATEOF(respValue[0]) != MCI_CARDSTATE_STBY) { /* Should be in STANDBY state now and ready */ retval = MCI_FUNC_ERR_STATE; } else { return(MCI_FUNC_OK); } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } if(retval == MCI_FUNC_ERR_STATE) { printf("%s() failed with MCI_FUNC_ERR_STATE\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Get the status of the card. The return is from the card. * By sending CMD13 (SEND_STATUS), the status of the card * will be responded from card addressed * * \param[out] cardStatus the status returned from the card * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciGetCardStatus(int32_t* cardStatus) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; uint32_t CmdArgument; int32_t retval = MCI_FUNC_FAILED; if ((MCI_CardType == MCI_SDSC_V1_CARD) || (MCI_CardType == MCI_SDSC_V2_CARD) || (MCI_CardType == MCI_SDHC_SDXC_CARD)) { CmdArgument = CardRCA; } else /* if MMC or unknown card type, use default RCA addr. */ { CmdArgument = 0x00010000; } /* * Note that, since it's called before the block write and read, this timeout * is important based on the clock you set for the data communication. */ retryCount = 0x300; while (retryCount > 0) { /* Clear previous status */ LPC_MCI->CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END); /* Send SELECT_CARD command before read and write */ respStatus = Lpc177x_8x_MciCmdResp(CMD13_SEND_STATUS, CmdArgument, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus) { retval = MCI_FUNC_FAILED; } /* // Don't check the response here already, that is not the responsibility of this routine. // The caller routine (in this case: GetStatus() is responsible for checking the value else if (!(respValue[0] & _SHIFT(MCI_CARDSTATUS_READYFORDATA_P0S))) { retval = MCI_FUNC_NOT_READY; } */ else { /* The ready bit should be set, it should be in either TRAN or RCV state now */ if (cardStatus != NULL) { *cardStatus = respValue[0]; } return(MCI_FUNC_OK); } retryCount--; NutMicroDelay(200); } #ifdef NUTDEBUG if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Set the length for the blocks in the next action on data * manipulation (as read, write, erase). This function is to * send CMD16 (SET_BLOCK_LEN) to cards. * * \param[in] blockLength the value for the length of block will be handled * * \return MCI_FUNC_OK if all success * * \note CMD16 command should be sent after the card is selected by CMD7 * (SELECT_CARD). ****************************************************************************/ int32_t Lpc177x_8x_MciSetBlockLen(uint32_t blockLength) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; int32_t retval = MCI_FUNC_FAILED; retryCount = 0x20; while (retryCount > 0) { /* Send SET_BLOCK_LEN command before read and write */ LPC_MCI->CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END); respStatus = Lpc177x_8x_MciCmdResp(CMD16_SET_BLOCK_LEN, blockLength, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus) { retval = MCI_FUNC_FAILED; } else if (!(respValue[0] & _SHIFT(MCI_CARDSTATUS_READYFORDATA_P0S))) { retval = MCI_FUNC_NOT_READY; } else if ((CARDSTATEOF(respValue[0]) != MCI_CARDSTATE_TRAN)) { retval = MCI_FUNC_ERR_STATE; } else { return(MCI_FUNC_OK); } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } if(retval == MCI_FUNC_ERR_STATE) { printf("%s() failed with MCI_FUNC_ERR_STATE\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Set bus-width (1 bit or 4 bit) to work with the card by command * CMD6 (SET_ACMD_BUS_WIDTH). * * \param[in] buswidth The value represented for bus-width * - 0b00: 1-bit bus-width * - 0b10: 4-bit bus-width * * \return MCI_FUNC_OK if all success * * \note * - If SD card is currently in used, it's possible to enable 4-bit * bus-width instead of 1-bit to speed up. * - This command can only be transferred during TRANS state. * - Since, it's a ACMD, CMD55 (APP_CMD) needs to be sent out first ****************************************************************************/ int32_t Lpc177x_8x_MciAcmd_SendBusWidth( uint32_t buswidth ) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; int32_t retval = MCI_FUNC_FAILED; retryCount = 0x20; /* reset retry counter */ while (retryCount > 0) { if (Lpc177x_8x_MciCmd_SendACMD() == MCI_FUNC_OK) { respStatus = Lpc177x_8x_MciCmdResp(ACMD6_SET_BUS_WIDTH, buswidth, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus) { retval = MCI_FUNC_FAILED; } else if (!(respValue[0] & _SHIFT(MCI_CARDSTATUS_READYFORDATA_P0S))) { retval = MCI_FUNC_NOT_READY; } else if ((CARDSTATEOF(respValue[0]) != MCI_CARDSTATE_TRAN)) { retval = MCI_FUNC_ERR_STATE; } else { return(MCI_FUNC_OK); } } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } if(retval == MCI_FUNC_ERR_STATE) { printf("%s() failed with MCI_FUNC_ERR_STATE\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Get the state of data transfer to see if it is ended or not * * \param None * * \return Transfer state (stored by Mci_Data_Xfer_End variable) ****************************************************************************/ uint32_t Lpc177x_8x_MciGetDataXferEndState(void) { return(Mci_Data_Xfer_End); } /************************************************************************//** * \brief Get the error state of the lastest data transfer * * \param None * * \return Error state (stored by Mci_Data_Xfer_ERR variable) ****************************************************************************/ uint32_t Lpc177x_8x_MciGetXferErrState(void) { return(Mci_Data_Xfer_ERR); } /************************************************************************//** * \brief Stop the current transmission on the bus by sending command CMD12 * (STOP_TRANSMISSION). In this case, the card may be in a unknown * state. So that it need a warm reset for normal operation. * * \param[in] None * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciCmd_StopTransmission( void ) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; int32_t retval = MCI_FUNC_FAILED; retryCount = 0x20; while (retryCount > 0) { LPC_MCI->CLEAR = 0x7FF; respStatus = Lpc177x_8x_MciCmdResp(CMD12_STOP_TRANSMISSION, 0x00000000, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus) { retval = MCI_FUNC_FAILED; } else if (!(respValue[0] & _SHIFT(MCI_CARDSTATUS_READYFORDATA_P0S))) { retval = MCI_FUNC_NOT_READY; } else { return(MCI_FUNC_OK); } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Write blocks to card by sending command CMD24 (WRITE_BLOCK) or * command CMD25 (WRITE_MULTIPLE_BLOCK) followed by the blocks of * data to be written. * * \param[in] blockNum The block number to start writting * * \param[in] numOfBlock Determine how many blocks will be written (from the * starting block) * * \return MCI_FUNC_OK if all success * * \note These commands should be sent in TRANS state. ****************************************************************************/ int32_t Lpc177x_8x_MciCmd_WriteBlock(uint32_t blockNum, uint32_t numOfBlock) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; uint32_t commandID; int32_t retval = MCI_FUNC_FAILED; if (numOfBlock > 1) { commandID = CMD25_WRITE_MULTIPLE_BLOCK; } else { commandID = CMD24_WRITE_BLOCK; } retryCount = 0x20; while (retryCount > 0) { LPC_MCI->CLEAR = 0x7FF; respStatus = Lpc177x_8x_MciCmdResp(commandID, blockNum, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus) { retval = MCI_FUNC_FAILED; } else if (!(XSHIFT_(respValue[0], MCI_CARDSTATUS_READYFORDATA_P0S) & 0x01)) { retval = MCI_FUNC_NOT_READY; } else if ((CARDSTATEOF(respValue[0]) != MCI_CARDSTATE_TRAN)) { retval = MCI_FUNC_ERR_STATE; } else { /* ready and in TRAN state */ return(MCI_FUNC_OK); } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } if(retval == MCI_FUNC_ERR_STATE) { printf("%s() failed with MCI_FUNC_ERR_STATE\n", __FUNCTION__ ); } #endif return(retval); /* Fatal error */ } /************************************************************************//** * \brief Read blocks to card by sending CMD17 (READ_SINGLE_BLOCK) or * CMD18 (READ_MULTIPLE_BLOCK) commands followed by the blocks of * data to be read. * * \param[in] blockNum The block number to start reading * * \param[in] numOfBlock Determine how many blocks will be read (from the * starting block) * * \return MCI_FUNC_OK if all success * * \note These commands should be sent in TRANS state. ****************************************************************************/ int32_t Lpc177x_8x_MciCmd_ReadBlock(uint32_t blockNum, uint32_t numOfBlock) { uint32_t retryCount; uint32_t respStatus; uint32_t respValue[4]; uint32_t commandID; int32_t retval = MCI_FUNC_FAILED; if (numOfBlock > 1) { commandID = CMD18_READ_MULTIPLE_BLOCK; } else { commandID = CMD17_READ_SINGLE_BLOCK; } retryCount = 0x20; while (retryCount > 0) { LPC_MCI->CLEAR = 0x7FF; respStatus = Lpc177x_8x_MciCmdResp(commandID, blockNum, EXPECT_SHORT_RESP, (uint32_t *)&respValue[0], ALLOW_CMD_TIMER); if (respStatus) { retval = MCI_FUNC_FAILED; } else if (!(respValue[0] & _SHIFT(MCI_CARDSTATUS_READYFORDATA_P0S))) { retval = MCI_FUNC_NOT_READY; } else if ((CARDSTATEOF(respValue[0]) != MCI_CARDSTATE_TRAN))//((CARDSTATEOF(respValue[0]) != MCI_CARDSTATE_READY)) { retval = MCI_FUNC_ERR_STATE; } else { /* ready and in TRAN state */ return(MCI_FUNC_OK); } NutMicroDelay(2); retryCount--; } #ifdef NUTDEBUG if(retval == MCI_FUNC_FAILED) { printf("%s() failed with MCI_FUNC_FAILED\n", __FUNCTION__ ); } if(retval == MCI_FUNC_NOT_READY) { printf("%s() failed with MCI_FUNC_NOT_READY\n", __FUNCTION__ ); } if(retval == MCI_FUNC_ERR_STATE) { printf("%s() failed with MCI_FUNC_ERR_STATE\n", __FUNCTION__ ); } #endif return(retval); } /************************************************************************//** * \brief Write data at a specific address to starting block with number * of blocks will be written from first block * \details * - At preparation * - Set MCI data control register, data length and data timeout * - Send CMD24 (WRITE_BLOCK) or CMD25 (WRITE_MULTIPLE_BLOCK) * commands to card * - Enable interupt for MCI component * - At completion * - TX_ACTIVE interrupt is occured * - Write data to FIFO register continuously until the data block * length is reached * * \param[in] *memblock The pointer to location stored required data to be * written to card * * \param[in] blockNum The block number to start writting * * \param[in] numOfBlock Determine how many blocks will be written (from the * starting block) * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciWriteBlock(uint8_t* memblock, uint32_t blockNum, uint32_t numOfBlock) { uint32_t DataCtrl = 0; dataSrcBlock = memblock; LPC_MCI->CLEAR = 0x7FF; LPC_MCI->DATACTRL = 0; NutMicroDelay(1); /* * Below status check is redundant, but ensure card is in TRANS state * before writing and reading to from the card. */ if (Lpc177x_8x_MciCheckStatus() != MCI_FUNC_OK) { #ifdef NUTDEBUG printf("%s() failed I [blocknum %lu]\n", __FUNCTION__ , blockNum); #endif Lpc177x_8x_MciCmd_StopTransmission(); return(MCI_FUNC_FAILED); } LPC_MCI->DATATMR = DATA_TIMER_VALUE_W; LPC_MCI->DATALEN = BLOCK_LENGTH*numOfBlock; Mci_Data_Xfer_End = 1; Mci_Data_Xfer_ERR = 0; fifo_plane = 0; txBlockCnt = 0; Lpc177x_8x_MciTXEnable(); if (Lpc177x_8x_MciCmd_WriteBlock(blockNum, numOfBlock) != MCI_FUNC_OK) { #ifdef NUTDEBUG printf("%s() failed II [blocknum %lu]\n", __FUNCTION__ , blockNum); #endif return( MCI_FUNC_FAILED ); } /* set Write Enable, data direction and data length code */ DataCtrl |= MCI_DATACTRL_ENABLE | MCI_DATACTRL_DIR_TO_CARD | MCI_DTATCTRL_BLOCKSIZE(DATA_BLOCK_LEN); LPC_MCI->DATACTRL = DataCtrl; // Go! NutMicroDelay(10); return(MCI_FUNC_OK); } /************************************************************************//** * \brief Read data at a specific address to starting block with number * of blocks will be read from first block * * \details * - At preparation * - Set MCI data control register, data length and data timeout * - Send CMD17 (READ_SINGLE_BLOCK) or CMD18 (READ_MULTIPLE_BLOCK) * commands to card * - Enable interupt for MCI component * - At completion * - RX_ACTIVE interrupt is occured * - Read data from FIFO register continuously until the data block * length is reached to retrieve needed data * * \param[in] *destBlock The pointer to location will captured data read * from card * * \param[in] blockNum The block number to start reading * * \param[in] numOfBlock Determine how many blocks will be read (from the * starting block) * * \return MCI_FUNC_OK if all success ****************************************************************************/ int32_t Lpc177x_8x_MciReadBlock(uint8_t* destBlock, uint32_t blockNum, uint32_t numOfBlock) { uint32_t DataCtrl = 0; dataDestBlock = destBlock; LPC_MCI->CLEAR = 0x7FF; LPC_MCI->DATACTRL = 0; NutMicroDelay(1); /* * Below status check is redundant, but ensure card is in TRANS state * before writing and reading to from the card. */ if (Lpc177x_8x_MciCheckStatus() != MCI_FUNC_OK) { #ifdef NUTDEBUG printf("%s() failed I [blocknum %lu]\n", __FUNCTION__ , blockNum); #endif Lpc177x_8x_MciCmd_StopTransmission(); return(MCI_FUNC_FAILED); } LPC_MCI->DATATMR = DATA_TIMER_VALUE_R; LPC_MCI->DATALEN = BLOCK_LENGTH*numOfBlock; Mci_Data_Xfer_End = 1; Mci_Data_Xfer_ERR = 0; fifo_plane = 0; rxBlockCnt = 0; Lpc177x_8x_MciRXEnable(); if (Lpc177x_8x_MciCmd_ReadBlock(blockNum, numOfBlock) != MCI_FUNC_OK) { #ifdef NUTDEBUG printf("%s() failed II [blocknum %lu]\n", __FUNCTION__ , blockNum); #endif return(MCI_FUNC_FAILED); } // Read, enable, block transfer, and data length DataCtrl |= MCI_DATACTRL_ENABLE | MCI_DATACTRL_DIR_FROM_CARD | MCI_DTATCTRL_BLOCKSIZE(DATA_BLOCK_LEN); LPC_MCI->DATACTRL = DataCtrl; // Go! NutMicroDelay(10); return(MCI_FUNC_OK); } /************************************************************************//** * \brief Turn off the MCI power by disabling the Power Register for MCI * * \param None * * \return None ****************************************************************************/ void Lpc177x_8x_MciPowerOff(void) { LPC_MCI->POWER = 0; NutMicroDelay(10); return; } /************************************************************************//** * \brief Get the priority level of the SD interrupt * please note that only 1 level is present for _ALL_ different * types of SD-interrupts * * \param None * * \return the interrupt level [0..31] ****************************************************************************/ int Lpc177x_8x_MciIrqGetPriority() { return NutIrqGetPriority(&sig_MCI); } /************************************************************************//** * \brief Set the priority level of the SD interrupt * please note that only 1 level is present for _ALL_ different * types of SD-interrupts * * \param the interrupt level [0..31] * * \return None ****************************************************************************/ void Lpc177x_8x_MciIrqSetPriority(int priority) { if ((priority >= 0) && (priority < 32)) { NutIrqSetPriority(&sig_MCI, priority); } }