diff --git a/src/Makefile b/src/Makefile index 9586775e5..0f90bbf99 100644 --- a/src/Makefile +++ b/src/Makefile @@ -290,6 +290,7 @@ ifeq ($(PCB), ARM) BOARDSRC = board_ersky9x.cpp EXTRABOARDSRC = ersky9x/core_cm3.c ersky9x/board_lowlevel.c ersky9x/crt.c ersky9x/vectors_sam3s.c # ersky9x/ff.c ersky9x/diskio_sam3s.c ersky9x/Media.c ersky9x/ccsbcs.c ersky9x/sdcard.c ersky9x/MEDSdcard.c + CPPSRC += ersky9x/sdmmc_spi.c ersky9x/sdspi.c EEPROMSRC = eeprom_arm.cpp PULSESSRC = pulses_arm.cpp CPPSRC += ersky9x/audio.cpp haptic.cpp diff --git a/src/ersky9x/sdmmc_spi.c b/src/ersky9x/sdmmc_spi.c new file mode 100644 index 000000000..0e7bba831 --- /dev/null +++ b/src/ersky9x/sdmmc_spi.c @@ -0,0 +1,1410 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "sdmmc_spi.h" +#include "sdspi.h" +//#include +//#include +//#include + +#define TRACE_ERROR(x...) +#define TRACE_DEBUG(x...) +#define TRACE_INFO(x...) +#define SANITY_CHECK(x...) + +#include + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +// SD card operation states +#define SD_STATE_STBY 0 +#define SD_STATE_DATA 1 +#define SD_STATE_RCV 2 + +// Card type +#define UNKNOWN_CARD 0 +#define CARD_SD 1 +#define CARD_SDHC 2 +#define CARD_MMC 3 + +// Delay between sending MMC commands +#define MMC_DELAY 0x4FF + +#define SD_ADDRESS(pSd, address) (((pSd)->cardType == CARD_SDHC) ? \ + (address):((address) << SD_BLOCK_SIZE_BIT)) + +//----------------------------------------------------------------------------- +/// MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS +/// R1 is the low order byte; R2 is the next highest byte, when present. +//----------------------------------------------------------------------------- +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +// R1 bit 7 is always zero +#define R2_SPI_CARD_LOCKED (1 << 0) +#define R2_SPI_WP_ERASE_SKIP (1 << 1) +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 2) +#define R2_SPI_CC_ERROR (1 << 3) +#define R2_SPI_CARD_ECC_ERROR (1 << 4) +#define R2_SPI_WP_VIOLATION (1 << 5) +#define R2_SPI_ERASE_PARAM (1 << 6) +#define R2_SPI_OUT_OF_RANGE (1 << 7) +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + +// Status register constants +#define STATUS_READY_FOR_DATA (1 << 8) +#define STATUS_IDLE (0 << 9) +#define STATUS_READY (1 << 9) +#define STATUS_IDENT (2 << 9) +#define STATUS_STBY (3 << 9) +#define STATUS_TRAN (4 << 9) +#define STATUS_DATA (5 << 9) +#define STATUS_RCV (6 << 9) +#define STATUS_PRG (7 << 9) +#define STATUS_DIS (8 << 9) +#define STATUS_STATE (0xF << 9) + +//----------------------------------------------------------------------------- +/// OCR Register +//----------------------------------------------------------------------------- +#define AT91C_VDD_16_17 (1 << 4) +#define AT91C_VDD_17_18 (1 << 5) +#define AT91C_VDD_18_19 (1 << 6) +#define AT91C_VDD_19_20 (1 << 7) +#define AT91C_VDD_20_21 (1 << 8) +#define AT91C_VDD_21_22 (1 << 9) +#define AT91C_VDD_22_23 (1 << 10) +#define AT91C_VDD_23_24 (1 << 11) +#define AT91C_VDD_24_25 (1 << 12) +#define AT91C_VDD_25_26 (1 << 13) +#define AT91C_VDD_26_27 (1 << 14) +#define AT91C_VDD_27_28 (1 << 15) +#define AT91C_VDD_28_29 (1 << 16) +#define AT91C_VDD_29_30 (1 << 17) +#define AT91C_VDD_30_31 (1 << 18) +#define AT91C_VDD_31_32 (1 << 19) +#define AT91C_VDD_32_33 (1 << 20) +#define AT91C_VDD_33_34 (1 << 21) +#define AT91C_VDD_34_35 (1 << 22) +#define AT91C_VDD_35_36 (1 << 23) +#define AT91C_CARD_POWER_UP_BUSY (1 << 31) + +#define AT91C_MMC_HOST_VOLTAGE_RANGE (AT91C_VDD_27_28 +\ + AT91C_VDD_28_29 +\ + AT91C_VDD_29_30 +\ + AT91C_VDD_30_31 +\ + AT91C_VDD_31_32 +\ + AT91C_VDD_32_33) +#define AT91C_CCS (1 << 30) + +// SPI_CMD Register Value +#define AT91C_POWER_ON_INIT (0) + +//----------------------------------------------------------------------------- +// Command Classes +//----------------------------------------------------------------------------- +// +// Class 0, 2, 4, 5, 7 and 8 are mandatory and shall be supported by all SD Memory Cards. +// Basic Commands (class 0) +// +// Cmd0 MCI + SPI +#define AT91C_GO_IDLE_STATE_CMD (0) +// Cmd1 SPI +#define AT91C_MMC_SEND_OP_COND_CMD (1) +// Cmd2 MCI +#define AT91C_ALL_SEND_CID_CMD (2) +// Cmd3 MCI +#define AT91C_SET_RELATIVE_ADDR_CMD (3) +// Cmd4 MCI +//#define AT91C_SET_DSR_CMD (4) +// cmd7 MCI +#define AT91C_SEL_DESEL_CARD_CMD (7) +// Cmd8 MCI + SPI +#define AT91C_SEND_IF_COND (8) +// Cmd9 MCI + SPI +#define AT91C_SEND_CSD_CMD (9) +// Cmd10 MCI + SPI +#define AT91C_SEND_CID_CMD (10) +// Cmd12 MCI + SPI +#define AT91C_STOP_TRANSMISSION_CMD (12) +// Cmd13 MCI + SPI +#define AT91C_SEND_STATUS_CMD (13) +// Cmd15 MCI +//#define AT91C_GO_INACTIVE_STATE_CMD (15) +// Cmd58 SPI +#define AT91C_READ_OCR_CMD (58) +// Cmd59 SPI +#define AT91C_CRC_ON_OFF_CMD (59) +//#define AT91C_MMC_ALL_SEND_CID_CMD (2) +//#define AT91C_MMC_SET_RELATIVE_ADDR_CMD (3) +//#define AT91C_MMC_READ_DAT_UNTIL_STOP_CMD (11) +//#define AT91C_STOP_TRANSMISSION_SYNC_CMD (12) + +//*------------------------------------------------ +//* Class 2 commands: Block oriented Read commands +//*------------------------------------------------ +// Cmd16 +#define AT91C_SET_BLOCKLEN_CMD (16) +// Cmd17 +#define AT91C_READ_SINGLE_BLOCK_CMD (17) +// Cmd18 +#define AT91C_READ_MULTIPLE_BLOCK_CMD (18) + +//*------------------------------------------------ +//* Class 4 commands: Block oriented write commands +//*------------------------------------------------ +// Cmd24 +#define AT91C_WRITE_BLOCK_CMD (24) +// Cmd25 +#define AT91C_WRITE_MULTIPLE_BLOCK_CMD (25) +// Cmd27 +//#define AT91C_PROGRAM_CSD_CMD (27) + +//*---------------------------------------- +//* Class 5 commands: Erase commands +//*---------------------------------------- +// Cmd32 +//#define AT91C_TAG_SECTOR_START_CMD (32) +// Cmd33 +//#define AT91C_TAG_SECTOR_END_CMD (33) +// Cmd38 +//#define AT91C_ERASE_CMD (38) + +//*---------------------------------------- +//* Class 7 commands: Lock commands +//*---------------------------------------- +// Cmd42 +//#define AT91C_LOCK_UNLOCK (42) + +//*----------------------------------------------- +// Class 8 commands: Application specific commands +//*----------------------------------------------- +// Cmd55 +#define AT91C_APP_CMD (55) +// cmd 56 +//#define AT91C_GEN_CMD (56) +// ACMD6 +#define AT91C_SDCARD_SET_BUS_WIDTH_CMD (6) +// ACMD13 +//#define AT91C_SDCARD_STATUS_CMD (13) +// ACMD22 +//#define AT91C_SDCARD_SEND_NUM_WR_BLOCKS_CMD (22) +// ACMD23 +//#define AT91C_SDCARD_SET_WR_BLK_ERASE_COUNT_CMD (23) +// ACMD41 +#define AT91C_SDCARD_APP_OP_COND_CMD (41) +// ACMD42 +//#define AT91C_SDCARD_SET_CLR_CARD_DETECT_CMD (42) +// ACMD51 +#define AT91C_SDCARD_SEND_SCR_CMD (51) + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Sends the current SD card driver command to the card. +/// Returns 0 if successful; Otherwise, returns the transfer status code or +/// SD_ERROR_DRIVER if there was a problem with the SD transfer. +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char SendCommand(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + SdDriver *pSdDriver = pSd->pSdDriver; + unsigned char error; + unsigned int i; + + // Send command + SDSPI_NCS((SdSpi *)pSdDriver); + + error = SDSPI_SendCommand((SdSpi *)pSdDriver, (SdSpiCmd *)pCommand); + if (error) { + TRACE_ERROR("SPI SendCommand: Failed to send command (%d)\n\r", error); + return SD_ERROR_DRIVER; + } + + // Wait for command to complete + while (!SDSPI_IsTxComplete((SdSpiCmd *)pCommand)); + if(pCommand->cmd == AT91C_STOP_TRANSMISSION_CMD) { + if( 1 == SDSPI_Wait((SdSpi *)pSdDriver, 2) ) { + TRACE_ERROR("SPI \n\r"); + } + while (SDSPI_WaitDataBusy((SdSpi *)pSdDriver) == 1); + } + + // Delay between sending commands, only for MMC card test. + if((pSd->cardType == CARD_MMC) + ||(pSd->cardType == UNKNOWN_CARD) + ||(pSd->cardType == CARD_SD)) { + + for(i=0; i < MMC_DELAY; i++); + } + + return pCommand->status; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void DecodeR1(unsigned char R1) +{ + if( (R1 & R1_SPI_IDLE)==R1_SPI_IDLE) { + TRACE_DEBUG("R1_SPI_IDLE\n\r"); + } + if( (R1 & R1_SPI_ERASE_RESET)==R1_SPI_ERASE_RESET) { + TRACE_DEBUG("R1_SPI_ERASE_RESET\n\r"); + } + if( (R1 & R1_SPI_ILLEGAL_COMMAND)==R1_SPI_ILLEGAL_COMMAND) { + TRACE_DEBUG("R1_SPI_ILLEGAL_COMMAND\n\r"); + } + if( (R1 & R1_SPI_COM_CRC)==R1_SPI_COM_CRC) { + TRACE_DEBUG("R1_SPI_COM_CRC\n\r"); + } + if( (R1 & R1_SPI_ERASE_SEQ)==R1_SPI_ERASE_SEQ) { + TRACE_DEBUG("R1_SPI_ERASE_SEQ\n\r"); + } + if( (R1 & R1_SPI_ADDRESS)==R1_SPI_ADDRESS) { + TRACE_DEBUG("R1_SPI_ADDRESS\n\r"); + } + if( (R1 & R1_SPI_PARAMETER)==R1_SPI_PARAMETER) { + TRACE_DEBUG("R1_SPI_PARAMETER\n\r"); + } +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void DecodeR2(unsigned char R2) +{ + if( (R2 & R2_SPI_CARD_LOCKED)==R2_SPI_CARD_LOCKED) { + TRACE_DEBUG("R2_SPI_CARD_LOCKED\n\r"); + } + if( (R2 & R2_SPI_WP_ERASE_SKIP)==R2_SPI_WP_ERASE_SKIP) { + TRACE_DEBUG("R2_SPI_WP_ERASE_SKIP/R2_SPI_LOCK_UNLOCK_FAIL\n\r"); + } + if( (R2 & R2_SPI_ERROR)==R2_SPI_ERROR) { + TRACE_DEBUG("R2_SPI_ERROR\n\r"); + } + if( (R2 & R2_SPI_CC_ERROR)==R2_SPI_CC_ERROR) { + TRACE_DEBUG("R2_SPI_CC_ERROR\n\r"); + } + if( (R2 & R2_SPI_CARD_ECC_ERROR)==R2_SPI_CARD_ECC_ERROR) { + TRACE_DEBUG("R2_SPI_CARD_ECC_ERROR\n\r"); + } + if( (R2 & R2_SPI_WP_VIOLATION)==R2_SPI_WP_VIOLATION) { + TRACE_DEBUG("R2_SPI_WP_VIOLATION\n\r"); + } + if( (R2 & R2_SPI_ERASE_PARAM)==R2_SPI_ERASE_PARAM) { + TRACE_DEBUG("R2_SPI_ERASE_PARAM\n\r"); + } + if( (R2 & R2_SPI_OUT_OF_RANGE)==R2_SPI_OUT_OF_RANGE) { + TRACE_DEBUG("R2_SPI_OUT_OF_RANGE/R2_SPI_CSD_OVERWRITE\n\r"); + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 1. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R1(unsigned char *pResp) +{ + DecodeR1(*pResp); + + if((*pResp & 0x7E) !=0) { + // An error occured + return SD_ERROR_NORESPONSE; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 1b. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R1b(unsigned char *pResp) +{ + // A zero value indicates card is busy. + // A non-zero value indicates the card is ready for the next command. + if( (*pResp) == 0 ) { + TRACE_INFO("Card is busy\n\r"); + } + + DecodeR1(*(pResp+1)); + if(((*(pResp+1)) & 0x7E) !=0) { + // An error occured + return SD_ERROR_NORESPONSE; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 2. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R2(unsigned char *pResp) +{ + DecodeR1(*pResp); + DecodeR2(*(pResp+1)); + + if((( *pResp & 0x7e ) != 0) && (*(pResp+1) != 0)) { + return SD_ERROR_NORESPONSE; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 3. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R3(unsigned char *pResp, unsigned int *ocr) +{ + // *pResp: bit 32-39: R1 + if(( *pResp & 0x7e ) != 0) { + return SD_ERROR_NORESPONSE; + } + else { + // bit 0-31: OCR + *ocr = ((*(pResp+1) << 24) \ + |(*(pResp+2) << 16) \ + |(*(pResp+3) << 8) \ + | *(pResp+4)); + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Check SPI mode response 7. +/// Returns 0 if no error; Otherwise, returns error. +/// \param pResp Pointer to response token. +//------------------------------------------------------------------------------ +static unsigned char SD_SPI_R7(unsigned char *pResp, unsigned char *isSdhc) +{ + *isSdhc = 0; + + if(( *pResp & 0x7e ) != 0) { + return SD_ERROR_NORESPONSE; + } + else { + // *(pResp+4): bit 0- 7: check pattern + // *(pResp+3): bit 8-11: voltage accepted: 0x01: 2.7-3.6V + if ((*(pResp+3) == 0x1) && (*(pResp+4) == 0xAA)) { + *isSdhc = 1; + } + else { + *isSdhc = 0; + } + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp +/// up time. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char Pon(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + + TRACE_DEBUG("Pon()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_POWER_ON_INIT; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + error = SendCommand(pSd); + if( error == 0 ) { + error = SD_SPI_R1((unsigned char *)&response); + } + return error; +} + +//------------------------------------------------------------------------------ +/// Resets all cards to idle state +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd0(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response; + unsigned char error; + + TRACE_DEBUG("Cmd0()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_GO_IDLE_STATE_CMD; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Set SD command state + pSd->state = SD_STATE_STBY; + + // send command + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// MMC send operation condition command. +/// Sends host capacity support information and activates the card's +/// initialization process. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SdCard driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd1(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd1()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_MMC_SEND_OP_COND_CMD; + pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Set SD command state + pSd->state = SD_STATE_STBY; + + // send command + error = SendCommand(pSd); + if (error) { + return error; + } + + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Sends SD Memory Card interface +/// condition, which includes host supply +/// voltage information and asks the card +/// whether card supports voltage. +/// Returns 0 if successful; otherwise returns SD_ERROR_NORESPONSE if the card did +/// not answer the command, or SD_ERROR_DRIVER. +/// \param pSd Pointer to a SD card driver instance. +/// \param supplyVoltage Expected supply voltage. +//------------------------------------------------------------------------------ +static unsigned char Cmd8(SdCard *pSd, unsigned char supplyVoltage) +{ + SdCmd *pCommand = &(pSd->command); + unsigned int response[2]; + unsigned char error; + unsigned char isSdhc; + + TRACE_DEBUG("Cmd8()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SEND_IF_COND; + pCommand->arg = (supplyVoltage << 8) | (0xAA); + pCommand->resType = 7; + pCommand->pResp = &response[0]; + // Set SD command state + pSd->state = SD_STATE_STBY; + + TRACE_DEBUG("supplyVoltage: 0x%x\n\r", supplyVoltage); + + // Send command + error = SendCommand(pSd); + + + TRACE_DEBUG("SD_R7[0]: 0x%x\n\r", response[0]); + TRACE_DEBUG("SD_R7[1]: 0x%x\n\r", response[1]); + error = SD_SPI_R7((unsigned char *)&response, &isSdhc); + if( error == SD_ERROR_NORESPONSE ) { + TRACE_DEBUG("Cmd8 R7 error:%d \n\r", error); + return error; + } + else { + if(isSdhc == 1) { + TRACE_DEBUG("Cmd8 Ver 2.00 isSdhc:%d\n\r", isSdhc); + return 0; + } + else { + TRACE_DEBUG("Cmd8 Ver 1.X isSdhc:%d\n\r", isSdhc); + return error; + } + } +} + +//------------------------------------------------------------------------------ +/// Addressed card sends its card-specific +/// data (CSD) on the CMD line. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd9(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response = 0; + unsigned char csdData[16]; + unsigned int i; + + TRACE_DEBUG("Cmd9()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SEND_CSD_CMD; + pCommand->resType = 1; + pCommand->blockSize = 16; + pCommand->pData = csdData; + pCommand->isRead = 1; + pCommand->pResp = &response; + + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + error = SendCommand(pSd); + + // In SPI mode, reading CSD is the same as reading data. + for (i = 0; i < 4; i++) { + pSd->csd[i] = csdData[i*4] << 24 | + csdData[i*4+1] << 16 | + csdData[i*4+2] << 8 | + csdData[i*4+3]; + } + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Forces the card to stop transmission +/// \param pSd Pointer to a SD card driver instance. +/// \param pStatus Pointer to a status variable. +//------------------------------------------------------------------------------ +static unsigned char Cmd12(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd12()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_STOP_TRANSMISSION_CMD; + pCommand->conTrans = SPI_NEW_TRANSFER; + pCommand->resType = 1; + pCommand->pResp = &response; + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + error = SendCommand(pSd); + + //TRACE_DEBUG("cmd12 resp 0x%X\n\r",response); + error = SD_SPI_R1b((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Addressed card sends its status register. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param pStatus Pointer to a status variable. +//------------------------------------------------------------------------------ +static unsigned char Cmd13(SdCard *pSd, unsigned int *pStatus) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + + TRACE_DEBUG("Cmd13()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SEND_STATUS_CMD; + pCommand->resType = 2; + pCommand->pResp = pStatus; + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R2((unsigned char *)pStatus); + return error; +} + +//------------------------------------------------------------------------------ +/// 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. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockLength Block length in bytes. +//------------------------------------------------------------------------------ +static unsigned char Cmd16(SdCard *pSd, unsigned short blockLength) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd16()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SET_BLOCKLEN_CMD; + pCommand->arg = blockLength; + pCommand->resType = 1; + pCommand->pResp = &response; + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + //return SendCommand(pSd); + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Continously transfers datablocks from card to host until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char Cmd18(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + //TRACE_DEBUG("Cmd18()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_READ_MULTIPLE_BLOCK_CMD; + pCommand->arg = address; + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = pData; + pCommand->isRead = 1; + pCommand->conTrans = SPI_NEW_TRANSFER; + pCommand->resType = 1; + pCommand->pResp = &response; + // Set SD command state + pSd->state = SD_STATE_DATA; + + // Send command + // return SendCommand(pSd); + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Write block command +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char Cmd25(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd25()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_WRITE_MULTIPLE_BLOCK_CMD; + pCommand->arg = address; + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = (unsigned char *) pData; + pCommand->conTrans = SPI_NEW_TRANSFER; + pCommand->resType = 1; + pCommand->pResp = &response; + + // Set SD command state + pSd->state = SD_STATE_RCV; + + // Send command + //return SendCommand(pSd); + error = SendCommand(pSd); + + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + + +//------------------------------------------------------------------------------ +/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply +/// ramp up time. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +//------------------------------------------------------------------------------ +static unsigned char Cmd55(SdCard *pSd) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd55()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_APP_CMD; + pCommand->resType = 1; + pCommand->pResp = &response; + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + //return SendCommand(pSd); + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// SPI Mode, Reads the OCR register of a card +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param pOcr OCR value of the card +//------------------------------------------------------------------------------ +static unsigned char Cmd58(SdCard *pSd, unsigned int *pOcr) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response[2]; + + TRACE_DEBUG("Cmd58()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_READ_OCR_CMD; + pCommand->resType = 3; + pCommand->pResp = &response[0]; + + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R3((unsigned char *)&response, pOcr); + return error; +} + +//------------------------------------------------------------------------------ +/// SPI Mode, Set CRC option of a card +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param option CRC option, 1 to turn on, 0 to trun off +//------------------------------------------------------------------------------ +static unsigned char Cmd59(SdCard *pSd, unsigned char option) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + TRACE_DEBUG("Cmd59()\n\r"); + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_CRC_ON_OFF_CMD; + pCommand->arg = (option & 0x1); + pCommand->resType = 1; + pCommand->pResp = &response; + + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + error = SendCommand(pSd); + error = SD_SPI_R1((unsigned char *)&response); + return error; +} + +//------------------------------------------------------------------------------ +/// Asks to all cards to send their operations conditions. +/// Returns the command transfer result (see SendCommand). +/// \param pSd Pointer to a SD card driver instance. +/// \param hcs Shall be true if Host support High capacity. +/// \param pCCS Set the pointed flag to 1 if hcs != 0 and SD OCR CCS flag is set. +//------------------------------------------------------------------------------ +static unsigned char Acmd41(SdCard *pSd, unsigned char hcs, unsigned char *pCCS) +{ + SdCmd *pCommand = &(pSd->command); + unsigned char error; + unsigned int response; + + do { + error = Cmd55(pSd); + if (error) { + return error; + } + + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->cmd = AT91C_SDCARD_APP_OP_COND_CMD; + pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; + if (hcs) { + pCommand->arg |= AT91C_CCS; + } + + pCommand->resType = 1; + pCommand->pResp = &response; + + // Set SD command state + pSd->state = SD_STATE_STBY; + + // Send command + TRACE_DEBUG("Acmd41()\n\r"); + error = SendCommand(pSd); + if (error) { + return error; + } + error = SD_SPI_R1((unsigned char *)&response); + if (error) { + return error; + } + // continue if in idle mode + if ((response & 0x1) != 0) { // R1_SPI_IDLE + continue; + } + *pCCS = ((response & AT91C_CCS) != 0); + return 0; + } + while ((response & AT91C_CARD_POWER_UP_BUSY) != AT91C_CARD_POWER_UP_BUSY); + + return 0; +} + + +//------------------------------------------------------------------------------ +/// Continue to transfer datablocks from card to host until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char ContinuousRead(SdCard *pSd, + unsigned short nbBlock, + unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = pData; + pCommand->isRead = 1; + pCommand->conTrans = SPI_CONTINUE_TRANSFER; + // Set SD command state + pSd->state = SD_STATE_DATA; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Continue to transfer datablocks from host to card until interrupted by a +/// STOP_TRANSMISSION command. +/// \param pSd Pointer to a SD card driver instance. +/// \param blockSize Block size (shall be set to 512 in case of high capacity). +/// \param pData Pointer to the application buffer to be filled. +/// \param address SD card address. +//------------------------------------------------------------------------------ +static unsigned char ContinuousWrite(SdCard *pSd, + unsigned short nbBlock, + const unsigned char *pData, + unsigned int address) +{ + SdCmd *pCommand = &(pSd->command); + + memset(pCommand, 0, sizeof(SdCmd)); + // Fill command information + pCommand->blockSize = SD_BLOCK_SIZE; + pCommand->nbBlock = nbBlock; + pCommand->pData = (unsigned char *) pData; + pCommand->isRead = 0; + pCommand->conTrans = SPI_CONTINUE_TRANSFER; + // Set SD command state + pSd->state = SD_STATE_RCV; + + // Send command + return SendCommand(pSd); +} + +//------------------------------------------------------------------------------ +/// Move SD card to transfer state. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the transfer command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to transfer. +/// \param nbBlocks Number of blocks to be transfer. +/// \param pData Data buffer whose size is at least the block size. +/// \param isRead 1 for read data and 0 for write data. +//------------------------------------------------------------------------------ +static unsigned char MoveToTransferState(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData, + unsigned char isRead) +{ + unsigned int status; + unsigned char error; + SdDriver *pSdDriver = pSd->pSdDriver; + + if((pSd->state == SD_STATE_DATA) + || (pSd->state == SD_STATE_RCV)) { + + // SD SPI mode uses stop transmission token to stop multiple block write. + if ((pSd->state == SD_STATE_RCV) ) { //&& (pSd->mode == SD_SPI_MODE)) { + SDSPI_StopTranToken((SdSpi *)pSdDriver); + pSd->state = SD_STATE_STBY; + while (SDSPI_WaitDataBusy((SdSpi *)pSdDriver) == 1); + while (SDSPI_WaitDataBusy((SdSpi *)pSdDriver) == 1); + } + else { + error = Cmd12(pSd); + if (error) { + return error; + } + } + } + pSd->preBlock = address + (nbBlocks-1); + + if(isRead) { + //TRACE_DEBUG("Read\n\r"); + // Wait for card to be ready for data transfers + do { + //TRACE_DEBUG("state = 0x%X\n\r", (status & STATUS_STATE) >> 9); + error = Cmd13(pSd, &status); + if (error) { + TRACE_DEBUG("Pb MTTS cmd13\n\r"); + return error; + } + break; + } + while (((status & STATUS_READY_FOR_DATA) == 0) || + ((status & STATUS_STATE) != STATUS_TRAN)); + // Read data + // Move to Sending data state + error = Cmd18(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + if (error) { + return error; + } + } + else { + //TRACE_DEBUG("Write\n\r"); + // Wait for card to be ready for data transfers + do { + error = Cmd13(pSd, &status); + if (error) { + TRACE_DEBUG("error cmd 13\n\r"); + return error; + } + break; + } + while ((status & STATUS_READY_FOR_DATA) == 0); + + // Move to Sending data state + error = Cmd25(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + if (error) { + TRACE_DEBUG("error cmd 25\n\r"); + return error; + } + } + + return error; +} + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Read Block of data in a buffer pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an code describing the error. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of the block to read. +/// \param nbBlocks Number of blocks to be read. +/// \param pData Data buffer whose size is at least the block size. +//------------------------------------------------------------------------------ +unsigned char SD_ReadBlock(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData) +{ + unsigned char error; + + SANITY_CHECK(pSd); + SANITY_CHECK(pData); + SANITY_CHECK(nbBlocks); + + if((pSd->state == SD_STATE_DATA) + && ((pSd->preBlock + 1) == address)) { + +#if defined(at91rm9200) + error = Cmd12(pSd); + if (error) { + return error; + } +#else + TRACE_DEBUG("SD_ReadBlock:ContinuousRead\n\r"); + error = ContinuousRead(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + pSd->preBlock = address + (nbBlocks-1); +#endif + } + else { + error = MoveToTransferState(pSd, address, nbBlocks, pData, 1); + } + return error; +} + +//------------------------------------------------------------------------------ +/// Write Block of data pointed by pData. The buffer size must be at +/// least 512 byte long. This function checks the SD card status register and +/// address the card if required before sending the read command. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param address Address of block to write. +/// \param nbBlocks Number of blocks to be read +/// \param pData Pointer to a 512 bytes buffer to be transfered +//------------------------------------------------------------------------------ +unsigned char SD_WriteBlock(SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + const unsigned char *pData) +{ + unsigned char error; + + SANITY_CHECK(pSd); + SANITY_CHECK(pData); + SANITY_CHECK(nbBlocks); + + if((pSd->state == SD_STATE_RCV) + && ((pSd->preBlock + 1) == address)) { + + TRACE_DEBUG("SD_WriteBlock:ContinuousWrite\n\r"); + error = ContinuousWrite(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); + pSd->preBlock = address + (nbBlocks-1); + } + else { + + //TRACE_FATAL("SD_WriteBlock:MoveToTransferState\n\r"); + error = MoveToTransferState(pSd, address, nbBlocks, + (unsigned char *)pData, 0); + } + return error; +} + + +//------------------------------------------------------------------------------ +/// Run the SDcard SPI Mode 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. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to SD driver already initialized. +//------------------------------------------------------------------------------ +unsigned char SD_SPI_Init(SdCard *pSd, SdDriver *pSpi) +{ + unsigned char isCCSet; + unsigned char error; + unsigned char cmd8Retries = 2; + unsigned char cmd1Retries = 1; + unsigned int pOCR; + + // The command GO_IDLE_STATE (CMD0) is the software reset command and sets card into Idle State + // regardless of the current card state. + error = Cmd0(pSd); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return 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... + do { + error = Cmd8(pSd, 1); + } + while ((error == SD_ERROR_NORESPONSE) && (cmd8Retries-- > 0)); + + if (error == SD_ERROR_NORESPONSE) { + // No response : Ver2.00 or later SD Memory Card(voltage mismatch) + // or Ver1.X SD Memory Card + // or not SD Memory Card + + TRACE_DEBUG("No response to Cmd8\n\r"); + + // CMD58 ? ! + error = Cmd58(pSd, &pOCR); + if (error) { + TRACE_ERROR("Error during initialization (%d), 8\n\r", error); + return error; + } + + // ACMD41 is a synchronization command used to negotiate the operation voltage range and to poll the + // cards until they are out of their power-up sequence. + error = Acmd41(pSd, 0, &isCCSet); + if (error) { + // Acmd41 failed : MMC card or unknown card + error = Cmd0(pSd); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + do { + error = Cmd1(pSd); + } + while ((error) && (cmd1Retries-- > 0)); + + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + else { + pSd->cardType = CARD_MMC; + } + } + else { + if(isCCSet == 0) { + TRACE_DEBUG("CARD SD\n\r"); + pSd->cardType = CARD_SD; + } + } + } + else if (!error) { + error = Cmd58(pSd, &pOCR); + if (error) { + TRACE_ERROR("Error during initialization (%d), 8\n\r", error); + return error; + } + + // Valid response : Ver2.00 or later SD Memory Card + error = Acmd41(pSd, 1, &isCCSet); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + error = Cmd58(pSd, &pOCR); + if (error) { + TRACE_ERROR("Error during initialization (%d), 8\n\r", error); + return error; + } + if (isCCSet) { + TRACE_DEBUG("CARD SDHC\n\r"); + pSd->cardType = CARD_SDHC; + } + else { + TRACE_DEBUG("CARD SD\n\r"); + pSd->cardType = CARD_SD; + } + } + else { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + + if (pSd->cardType != CARD_MMC) { + // The host issues CRC_ON_OFF (CMD59) to set data CRC on/off + // The host can turn the CRC option on and off using the CRC_ON_OFF command (CMD59). + // Host should enable CRC verification before issuing ACMD41. + error = Cmd59(pSd,0); // turn crc option OFF + if (error) { + + TRACE_ERROR("Error during initialization (%d)\n\r, 59", error); + return error; + } + } + + // The host issues SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register), + // e.g. block length, card storage capacity, etc... + error = Cmd9(pSd); + if (error) { + + TRACE_ERROR("Error during initialization (%d), 9\n\r", error); + return error; + } + return 0; +} +// + +//------------------------------------------------------------------------------ +/// 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. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to SD driver already initialized. +/// \param mode Select SD or SPI access mode +//------------------------------------------------------------------------------ +unsigned char SD_Init(SdCard *pSd, SdDriver *pSdDriver) +{ + unsigned char error; + + //TRACE_DEBUG("SD_Init()\n\r"); + + // Initialize SdCard structure + pSd->pSdDriver = pSdDriver; + pSd->cardAddress = 0; + pSd->preBlock = 0xffffffff; + pSd->state = SD_STATE_STBY; + pSd->cardType = UNKNOWN_CARD; + memset(&(pSd->command), 0, sizeof(SdCmd)); + + // 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 + //TRACE_DEBUG("Pon()\n\r"); + error = Pon(pSd); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + + // 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 = SD_SPI_Init(pSd, pSdDriver); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + + // 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 (pSd->cardType == CARD_SD) { + error = Cmd16(pSd, SD_BLOCK_SIZE); + if (error) { + TRACE_ERROR("Error during initialization (%d)\n\r", error); + return error; + } + } + + // If SD CSD v2.0 + if((pSd->cardType != CARD_MMC) && (SD_CSD_STRUCTURE(pSd) == 1)) { + pSd->totalSize = SD_CSD_TOTAL_SIZE_HC(pSd); + pSd->blockNr = SD_CSD_BLOCKNR_HC(pSd); + } + else { + pSd->totalSize = SD_CSD_TOTAL_SIZE(pSd); + pSd->blockNr = SD_CSD_BLOCKNR(pSd); + } + + if (pSd->cardType == UNKNOWN_CARD) { + return SD_ERROR_NOT_INITIALIZED; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Stop the SDcard. This function stops all SD operations. +/// Returns 0 if successful; otherwise returns an SD_ERROR code. +/// \param pSd Pointer to a SD card driver instance. +/// \param pSdDriver Pointer to MCI driver already initialized. +//------------------------------------------------------------------------------ +unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver) +{ + unsigned char error; + SdCmd *pCommand = &(pSd->command); + + SANITY_CHECK(pSd); + SANITY_CHECK(pSdDriver); + + if(pCommand->conTrans == SPI_CONTINUE_TRANSFER) + { + TRACE_DEBUG("SD_StopTransmission()\n\r"); + + error = Cmd12(pSd); + if(error) { + return error; + } + } + + SDSPI_Close((SdSpi *)pSdDriver); + return 0; +} + + diff --git a/src/ersky9x/sdmmc_spi.h b/src/ersky9x/sdmmc_spi.h new file mode 100644 index 000000000..efb740881 --- /dev/null +++ b/src/ersky9x/sdmmc_spi.h @@ -0,0 +1,443 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "sdmmc_spi" +/// +/// !Purpose +/// +/// Implementation for sdcard spi mode physical layer driver. Supply a set of sdcard spi mode's +/// interface. +/// +/// !Usage +/// +/// -# SD_Init: Run the SDcard initialization sequence +/// -# SD_SPI_Init : Run the SDcard SPI Mode initialization sequence +/// -# SD_Stop: Stop the SDcard by sending Cmd12 +/// -# SD_ReadBlock : Read blocks of data +/// -# SD_WriteBlock : Write blocks of data +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard spi mode initialization and identification" +/// +/// !Purpose +/// +/// sdcard spi mode initialization and identification sequence +/// +/// !Description +/// - Host sends Cmd0 to do card reset, card is in "idle state". +/// - Host sends Cmd8 and checks the response of the card, only sdcard supports physical +/// layer version 2.00 will response correctly to Cmd8, and this command is mandatory to be +/// sent before ACmd41 for sdcard which support physical layer version 2.00, to enable new +/// functions or to validate a dual-voltage card. +/// - refer to "If Cmd8 response ok" branch for the initialize of sdcard 2.0. +/// - refer to "If Cmd8 response fail" branch for the initialize of sdcard 1.x, mmc card, sdcard2.0 +/// with invalid voltage. +/// - If Cmd8 response ok +/// - Host sends Cmd58 to read OCR register to validate the voltage. +/// - If the response to Cmd58 is fail, initialize ends, the card is put into inactive state. +/// - If the response to Cmd58 is ok, continue to the next step. +/// - Host sends ACmd41* with argument "HCS" equal to "1". +/// - If the response to ACmd41 failed, it means the card does not match the voltage +/// desired by the host, the card will be put into inactive state, initialize ends. +/// - If the response with "CCS" equal to "1", the card is a version 2.0 high capacity sdcard, +/// refer to "Card Initialize" for the succeeding initialize sequence. +/// - If the response with "CCS" equal to "0", the card is a version 2.0 standard capacity sdcard. +/// refer to "Card Initialize" for the succeeding initialize sequence. +/// - If Cmd8 response fail +/// - Host sends Cmd58 to read OCR register to validate the voltage. +/// - If the response to Cmd58 is fail, initialize ends, the card is put into inactive state. +/// - If the response to Cmd58 is ok, continue to the next step. +/// - Host sends ACmd41* argument "HCS" equal to "0". +/// - If the response to ACmd41 ok, the card is a version 1.x sdcard, refer to "Card Initialize" for +/// the succeeding initialize sequence. +/// - If the response to ACmd41 fails +/// - Host sends Cmd0 to reset card. +/// - Host sends Cmd1 to card. +/// - If card has response to Cmd1, the card is a MMC card, refer to "Card Initialize" for the +/// succeeding initialize sequence. +/// - If card has no response to Cmd1, the card is either an unknown card or a card does +/// not match host's voltage, the initialize ends. +/// - Card Initialize +/// - At this stage, the initialization and identification process is over, the following steps are done +/// for the sdcard's succeeding operation. +/// - Host sends Cmd59 to turn sdcard's CRC option off. +/// - Host sends Cmd9 to get the Card Specific Data (CSD). +/// +/// \note Send Cmd55 before send ACmd41. \endnote +/// \note sdcard include ver 1.x sdcard, ver2.0 standard capacity sdcard, ver2.0 high capacity sdcard \endnote +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard spi mode write" +/// +/// !Purpose +/// +/// sdcard spi mode write process +/// +/// !Description +/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send +/// Cmd12 or use stop transmission token to stop multiple block write, and to transit sdcard to +/// "stand-by state". +/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". +/// - Host sends Cmd25 to do multiple blocks write, the address here is different between high capacity +/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's +/// address is equal to block number times 512. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdcard spi mode read" +/// +/// !Purpose +/// +/// sdcard spi mode read process +/// +/// !Description +/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send +/// Cmd12 or use stop transmission token to stop multiple block read and to transit sdcard to +/// "stand-by state". +/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". +/// - Host sends Cmd18 to do multiple blocks read, the address here is different between high capacity +/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's +/// address is equal to block number times 512. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "sdhc" +/// +/// !Purpose +/// +/// highlight of sdhc +/// +/// !Sdhc initialization and identification +/// +/// - Refer to page "sdcard spi mode initialization and identification" for the initialization and identification +/// sequence of a sdhc. +/// +/// !Functional difference between sdhc and standard capacity sdcard +/// +/// - Command argument is different: +/// - Sdhc uses block address format in memory access commands*, block length is fixed to 512 bytes. +/// - Standard capacity sdcard uses byte address format in memory access commands, block length +/// is defined in Cmd16. +/// - Partial access and misalign access are disabled in sdhc as the block address is used. +/// - Sdhc does not support write-protected commands (Cmd28, Cmd29, Cmd30). +/// +/// \note Memory access commands means block read commands (CMD17, CMD18), block write commands +/// (CMD24, CMD25), and block erase commands (CMD32, CMD33). +/// +//------------------------------------------------------------------------------ + + +#ifndef SDCARD_H +#define SDCARD_H + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + + +/// There was an error with the SD driver. +#define SD_ERROR_DRIVER 1 +/// The SD card did not answer the command. +#define SD_ERROR_NORESPONSE 2 +/// The SD card did not answer the command. +#define SD_ERROR_NOT_INITIALIZED 3 +/// The SD card is busy +#define SD_ERROR_BUSY 4 +/// The operation is not supported +#define SD_ERROR_NOT_SUPPORT 5 + +/// SD card block size in bytes. +#define SD_BLOCK_SIZE 512 +/// SD card block size binary shift value +#define SD_BLOCK_SIZE_BIT 9 + +//- MMC Card Command Types +/// Broadcast commands (bc), no response +#define MMC_CCT_BC 0 +/// Broadcase commands with response (bcr) +#define MMC_CCT_BCR 1 +/// Addressed commands (ac), no data transfer on DAT lines +#define MMC_CCT_AC 2 +/// Addressed data transfer commands (adtc), data transfer on DAT lines +#define MMC_CCT_ADTC 3 + +//- MMC Card Command Classes (CCC) +/// Class 0: basic +#define MMC_CCC_BASIC 0 +/// Class 1: stream read +#define MMC_CCC_STREAM_READ 1 +/// Class 2: block read +#define MMC_CCC_BLOCK_READ 2 +/// Class 3: stream write +#define MMC_CCC_STREAM_WRITE 3 +/// Class 4: block write +#define MMC_CCC_BLOCK_WRITE 4 +/// Class 5: erase +#define MMC_CCC_ERASE 5 +/// Class 6: write protection +#define MMC_CCC_WRITE_PROTECTION 6 +/// Class 7: lock card +#define MMC_CCC_LOCK_CARD 7 +/// Class 8: application specific +#define MMC_CCC_APP_SPEC 8 +/// Class 9: I/O mode +#define MMC_CCC_IO_MODE 9 + +//- MMC/SD Card Command Response Type +/// R1 (normal response command), 48bits +#define MMC_RESP_R1 1 +#define SD_RESP_R1 MMC_RESP_R1 +/// R1b: busy signal transmitted on the data line DAT0 +#define MMC_RESP_R1b 1 +#define SD_RESP_R1b MMC_RESP_R1b +/// R2: 136bits, CID, CSD register +#define MMC_RESP_R2 2 +#define SD_RESP_R2 MMC_RESP_R2 +/// R3: 48bits, OCR +#define MMC_RESP_R3 3 +#define SD_RESP_R3 MMC_RESP_R3 +/// R4 (Fast I/O), 48bits +#define MMC_RESP_R4 4 +/// R5 (Interrupt request), 48bits +#define MMC_RESP_R5 5 +/// R6 (Published RCA response), 48bits +#define SD_RESP_R6 6 +/// R7 (Card interface condition), 48bits +#define SD_RESP_R7 7 + +//- MMC Card CMD6 access mode +#define MMC_SWITCH_CMDSET 0 +#define MMC_SWITCH_SETBITS 1 +#define MMC_SWITCH_CLRBITS 2 +#define MMC_SWITCH_WRITE 3 + +//-MMC Boot partition enable +/// Boot partition 1 enabled for boot +#define MMC_BOOT_PART_1 1 +/// Boot partition 2 enabled for boot +#define MMC_BOOT_PART_2 2 +/// User area enabled for boot +#define MMC_BOOT_PART_USER 7 + +//-MMC Boot partition access +/// R/W boot partition 1 +#define MMC_BOOT_ACC_PART1 1 +/// R/W boot partition 2 +#define MMC_BOOT_ACC_PART2 2 + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +// CID register access macros. +#define SD_CID(pSd, bitfield, bits) ( (pSd->cid[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) +#define SD_CID_MID(pSd) SD_CID(pSd, 120, 8) ///< Manufacturer ID +#define SD_CID_BGA(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) +#define SD_CID_CBS(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) +#define SD_CID_OID_BYTE_1(pSd) SD_CID(pSd, 112, 8) ///< OEM/Application ID byte 1 +#define SD_CID_OID_BYTE_0(pSd) SD_CID(pSd, 104, 8) ///< OEM/Application ID byte 0 +#define SD_CID_PNM_BYTE_4(pSd) SD_CID(pSd, 96, 8) ///< Product revision byte 4 +#define SD_CID_PNM_BYTE_3(pSd) SD_CID(pSd, 88, 8) ///< Product revision byte 3 +#define SD_CID_PNM_BYTE_2(pSd) SD_CID(pSd, 80, 8) ///< Product revision byte 2 +#define SD_CID_PNM_BYTE_1(pSd) SD_CID(pSd, 72, 8) ///< Product revision byte 1 +#define SD_CID_PNM_BYTE_0(pSd) SD_CID(pSd, 64, 8) ///< Product revision byte 0 +#define SD_CID_PRV_1(pSd) SD_CID(pSd, 24, 8) ///< Product serial number 1 +#define SD_CID_PRV_2(pSd) SD_CID(pSd, 32,24) ///< Product serial number 2 +#define SD_CID_MDT_YEAR(pSd) (SD_CID(pSd, 12, 8))+2000///< Manufacturing date year +#define SD_CID_MDT_MONTH(pSd) SD_CID(pSd, 8, 4) ///< Manufacturing date month +#define SD_CID_CRC(pSd) SD_CID(pSd, 1, 7) ///< CRC7 checksum + +// CSD register access macros. +#define SD_CSD(pSd, bitfield, bits) ((((pSd)->csd)[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) +#define SD_CSD_STRUCTURE(pSd) SD_CSD(pSd, 126, 2) ///< CSD structure 00b Version 1.0 01b version 2.0 High Cap +#define SD_CSD_SPEC_VERS(pSd) SD_CSD(pSd, 122, 4) ///< System Specification Version Number +#define SD_CSD_TAAC(pSd) SD_CSD(pSd, 112, 8) ///< Data read-access-time-1 +#define SD_CSD_NSAC(pSd) SD_CSD(pSd, 104, 8) ///< Data read access-time-2 in CLK cycles +#define SD_CSD_TRAN_SPEED(pSd) SD_CSD(pSd, 96, 8) ///< Max. data transfer rate +#define SD_CSD_CCC(pSd) SD_CSD(pSd, 84, 12) ///< Card command class +#define SD_CSD_READ_BL_LEN(pSd) SD_CSD(pSd, 80, 4) ///< Max. read data block length +#define SD_CSD_READ_BL_PARTIAL(pSd) SD_CSD(pSd, 79, 1) ///< Bartial blocks for read allowed +#define SD_CSD_WRITE_BLK_MISALIGN(pSd) SD_CSD(pSd, 78, 1) ///< Write block misalignment +#define SD_CSD_READ_BLK_MISALIGN(pSd) SD_CSD(pSd, 77, 1) ///< Read block misalignment +#define SD_CSD_DSR_IMP(pSd) SD_CSD(pSd, 76, 1) ///< DSP implemented +#define SD_CSD_C_SIZE(pSd) ((SD_CSD(pSd, 72, 2) << 10) + \ + (SD_CSD(pSd, 64, 8) << 2) + \ + SD_CSD(pSd, 62, 2)) ///< Device size +#define SD_CSD_C_SIZE_HC(pSd) ((SD_CSD(pSd, 64, 6) << 16) + \ + (SD_CSD(pSd, 56, 8) << 8) + \ + SD_CSD(pSd, 48, 8)) ///< Device size v2.0 High Capacity +#define SD_CSD_VDD_R_CURR_MIN(pSd) SD_CSD(pSd, 59, 3) ///< Max. read current @VDD min +#define SD_CSD_VDD_R_CURR_MAX(pSd) SD_CSD(pSd, 56, 3) ///< Max. read current @VDD max +#define SD_CSD_VDD_W_CURR_MIN(pSd) SD_CSD(pSd, 53, 3) ///< Max. write current @VDD min +#define SD_CSD_VDD_W_CURR_MAX(pSd) SD_CSD(pSd, 50, 3) ///< Max. write current @VDD max +#define SD_CSD_C_SIZE_MULT(pSd) SD_CSD(pSd, 47, 3) ///< Device size multiplier +#define SD_CSD_ERASE_BLK_EN(pSd) SD_CSD(pSd, 46, 1) ///< Erase single block enable +#define SD_CSD_SECTOR_SIZE(pSd) ((SD_CSD(pSd, 40, 6) << 1) + SD_CSD(pSd, 39, 1)) ///< Erase sector size +#define SD_CSD_WP_GRP_SIZE(pSd) SD_CSD(pSd, 32, 7) ///< Write protect group size +#define SD_CSD_WP_GRP_ENABLE(pSd) SD_CSD(pSd, 31, 1) ///< write protect group enable +#define SD_CSD_R2W_FACTOR(pSd) SD_CSD(pSd, 26, 3) ///< Write speed factor +#define SD_CSD_WRITE_BL_LEN(pSd) ((SD_CSD(pSd, 24, 2) << 2) + SD_CSD(pSd, 22, 2)) ///< Max write block length +#define SD_CSD_WRITE_BL_PARTIAL(pSd) SD_CSD(pSd, 21, 1) ///< Partial blocks for write allowed +#define SD_CSD_CONTENT_PROT_APP(pSd) SD_CSD(pSd, 16, 1) ///< File format group +#define SD_CSD_FILE_FORMAT_GRP(pSd) SD_CSD(pSd, 15, 1) ///< File format group +#define SD_CSD_COPY(pSd) SD_CSD(pSd, 14, 1) ///< Copy flag (OTP) +#define SD_CSD_PERM_WRITE_PROTECT(pSd) SD_CSD(pSd, 13, 1) ///< Permanent write protect +#define SD_CSD_TMP_WRITE_PROTECT(pSd) SD_CSD(pSd, 12, 1) ///< Temporary write protection +#define SD_CSD_FILE_FORMAT(pSd) SD_CSD(pSd, 11, 2) ///< File format +#define SD_CSD_ECC(pSd) SD_CSD(pSd, 8, 2) ///< CRC +#define SD_CSD_CRC(pSd) SD_CSD(pSd, 1, 7) ///< CRC +#define SD_CSD_MULT(pSd) (1 << (SD_CSD_C_SIZE_MULT(pSd) + 2)) +#define SD_CSD_BLOCKNR(pSd) ((SD_CSD_C_SIZE(pSd) + 1) * SD_CSD_MULT(pSd)) +#define SD_CSD_BLOCKNR_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 1024) +#define SD_CSD_BLOCK_LEN(pSd) (1 << SD_CSD_READ_BL_LEN(pSd)) +#define SD_CSD_TOTAL_SIZE(pSd) (SD_CSD_BLOCKNR(pSd) * SD_CSD_BLOCK_LEN(pSd)) +#define SD_CSD_TOTAL_SIZE_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 512* 1024) +#define SD_TOTAL_SIZE(pSd) ((pSd)->totalSize) +#define SD_TOTAL_BLOCK(pSd) ((pSd)->blockNr) + +// SCR register access macros. +#define SD_SCR_BUS_WIDTHS(pScr) ((pScr[1] >> 16) & 0xF) ///< Describes all the DAT bus that are supported by this card +#define SD_SCR_BUS_WIDTH_4BITS (1 << 1) ///< 4bit Bus Width is supported +#define SD_SCR_BUS_WIDTH_1BIT (1 << 0) ///< 1bit Bus Width is supported + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// SD end-of-transfer callback function. +typedef void (*SdCallback)(unsigned char status, void *pCommand); + +//------------------------------------------------------------------------------ +/// SD Transfer Request prepared by the application upper layer. This structure +/// is sent to the SD_SendCommand function to start the transfer. At the end of +/// the transfer, the callback is invoked by the interrupt handler. +//------------------------------------------------------------------------------ +typedef struct _SdCmd { + + /// Command status. + volatile char status; + /// Command code. + unsigned int cmd; + /// Command argument. + unsigned int arg; + /// Data buffer. + unsigned char *pData; + /// Size of data buffer in bytes. + unsigned short blockSize; + /// Number of blocks to be transfered + unsigned short nbBlock; + /// Indicate if continue to transfer data + unsigned char conTrans; + /// Indicates if the command is a read operation. + unsigned char isRead; + /// Response buffer. + unsigned int *pResp; + /// SD card response type. + unsigned char resType; + /// Optional user-provided callback function. + SdCallback callback; + /// Optional argument to the callback function. + void *pArg; + +} SdCmd; + +//------------------------------------------------------------------------------ +/// SD driver structure. Holds the internal state of the SD driver and +/// prevents parallel access to a SPI peripheral. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to a SPI peripheral. + //AT91S_MCI *pSdHw; + /// SPI peripheral identifier. + unsigned char spiId; + /// MCI HW mode + unsigned char mciMode; + /// Pointer to currently executing command. + SdCmd *pCommand; + /// Mutex. + volatile char semaphore; + +} SdDriver; + +//------------------------------------------------------------------------------ +/// Sdcard driver structure. It holds the current command being processed and +/// the SD card address. +//------------------------------------------------------------------------------ +typedef struct _SdCard { + + /// Pointer to the underlying MCI driver. + SdDriver *pSdDriver; + /// Current MCI command being processed. + SdCmd command; + /// SD card current address. + unsigned short cardAddress; + /// Card-specific data. + unsigned int csd[4]; + /// Previous access block number. + unsigned int preBlock; + /// State after sd command complete + unsigned char state; + /// Card type + unsigned char cardType; + /// Card total size + unsigned int totalSize; + /// Card block number + unsigned int blockNr; + /// Card access mode + unsigned char mode; + +} SdCard; + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +extern unsigned char SD_Init(SdCard *pSd, + SdDriver *pSdDriver); + +extern unsigned char SD_ReadBlock( + SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + unsigned char *pData); + +extern unsigned char SD_WriteBlock( + SdCard *pSd, + unsigned int address, + unsigned short nbBlocks, + const unsigned char *pData); + +extern unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver); + +#endif //#ifndef SDCARD_H + diff --git a/src/ersky9x/sdspi.c b/src/ersky9x/sdspi.c new file mode 100644 index 000000000..52e9789b2 --- /dev/null +++ b/src/ersky9x/sdspi.c @@ -0,0 +1,781 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "sdspi.h" +//#include +//#include +//#include +//#include +//#include +//#include +//#include +#include + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Transfer is pending. +#define SDSPI_STATUS_PENDING 1 +/// Transfer has been aborted because an error occured. +#define SDSPI_STATUS_ERROR 2 + +/// SPI driver is currently in use. +#define SDSPI_ERROR_LOCK 1 + +// Data Tokens +#define SDSPI_START_BLOCK_1 0xFE // Single/Multiple read, single write +#define SDSPI_START_BLOCK_2 0xFC // Multiple block write +#define SDSPI_STOP_TRAN 0xFD // Cmd12 + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the SD Spi structure and the corresponding SPI hardware. +/// \param pSpid Pointer to a Spid instance. +/// \param pSpiHw Associated SPI peripheral. +/// \param spiId SPI peripheral identifier. +//------------------------------------------------------------------------------ +void SDSPI_Configure(SdSpi *pSdSpi, + AT91PS_SPI pSpiHw, + unsigned char spiId) +{ + // Initialize the SPI structure + pSdSpi->pSpiHw = pSpiHw; + pSdSpi->spiId = spiId; + pSdSpi->semaphore = 1; + + // Enable the SPI clock + // BSS replaced by below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + PMC->PMC_PCER0 = (1 << pSdSpi->spiId); + + // Execute a software reset of the SPI twice + pSpiHw->SPI_CR = SPI_CR_SWRST; // BSS AT91C_SPI_SWRST; + pSpiHw->SPI_CR = SPI_CR_SWRST; // BSS AT91C_SPI_SWRST; + + // Configure SPI in Master Mode with No CS selected !!! + pSpiHw->SPI_MR = SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PCS; // BSS AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS; + + // Disables the receiver PDC transfer requests + // Disables the transmitter PDC transfer requests. + pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Enable the SPI + pSpiHw->SPI_CR = SPI_CR_SPIEN; // BSS AT91C_SPI_SPIEN; + + // Disable the SPI clock + // BSS replaced by below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); + PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); +} + +//------------------------------------------------------------------------------ +/// Configures the parameters for the device corresponding to the cs. +/// \param pSdSpi Pointer to a SdSpi instance. +/// \param cs number corresponding to the SPI chip select. +/// \param csr SPI_CSR value to setup. +//------------------------------------------------------------------------------ +void SDSPI_ConfigureCS(SdSpi *pSdSpi, unsigned char cs, unsigned int csr) +{ + unsigned int spiMr; + AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + + // Enable the SPI clock + // BSS below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + PMC->PMC_PCER0 = (1 << pSdSpi->spiId); + + //TRACE_DEBUG("CSR[%d]=0x%8X\n\r", cs, csr); + pSpiHw->SPI_CSR[cs] = csr; + +//jcb to put in sendcommand + // Write to the MR register + spiMr = pSpiHw->SPI_MR; + spiMr |= SPI_MR_PCS; // TODO AT91C_SPI_PCS; + spiMr &= ~((1 << cs) << 16); + pSpiHw->SPI_MR = spiMr; + + // Disable the SPI clock + // BSS below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); + PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); +} + +//------------------------------------------------------------------------------ +/// Use PDC for SPI data transfer. +/// Return 0 if no error, otherwise return error status. +/// \param pSdSpi Pointer to a SdSpi instance. +/// \param pData Data pointer. +/// \param size Data transfer byte count. +//------------------------------------------------------------------------------ +unsigned char SDSPI_PDC(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) +{ + AT91PS_SPI pSpiHw = pSdSpi->pSpiHw; + unsigned int spiIer; + + if (pSdSpi->semaphore == 0) { + TRACE_DEBUG("No semaphore\n\r"); + return SDSPI_ERROR_LOCK; + } + pSdSpi->semaphore--; + + // Enable the SPI clock + // BSS below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + PMC->PMC_PCER0 = (1 << pSdSpi->spiId); + + // Disable transmitter and receiver + pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Receive Pointer Register + pSpiHw->SPI_RPR = (int)pData; + // Receive Counter Register + pSpiHw->SPI_RCR = size; + // Transmit Pointer Register + pSpiHw->SPI_TPR = (int) pData; + // Transmit Counter Register + pSpiHw->SPI_TCR = size; + + spiIer = SPI_IER_RXBUFF; // TODO AT91C_SPI_RXBUFF; + + // Enable transmitter and receiver + pSpiHw->SPI_PTCR = SPI_PTCR_RXTEN | SPI_PTCR_TXTEN; // BSS AT91C_PDC_RXTEN | AT91C_PDC_TXTEN; + + // Interrupt enable shall be done after PDC TXTEN and RXTEN + pSpiHw->SPI_IER = spiIer; + + return 0; +} + +//! Should be moved to a new file +//------------------------------------------------------------------------------ +/// Read data on SPI data bus; +/// Returns 1 if read fails, returns 0 if no error. +/// \param pSdSpi Pointer to a SD SPI driver instance. +/// \param pData Data pointer. +/// \param size Data size. +//------------------------------------------------------------------------------ +unsigned char SDSPI_Read(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) +{ + unsigned char error; + + // MOSI should hold high during read, or there will be wrong data in received data. + memset(pData, 0xff, size); + + error = SDSPI_PDC(pSdSpi, pData, size); + + while(SDSPI_IsBusy(pSdSpi) == 1); + + if( error == 0 ) { + return 0; + } + else { + TRACE_DEBUG("PB SDSPI_Read\n\r"); + return 1; + } +} + +//------------------------------------------------------------------------------ +/// Write data on SPI data bus; +/// Returns 1 if write fails, returns 0 if no error. +/// \param pSdSpi Pointer to a SD SPI driver instance. +/// \param pData Data pointer. +/// \param size Data size. +//------------------------------------------------------------------------------ +unsigned char SDSPI_Write(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) +{ + unsigned char error; + + error = SDSPI_PDC(pSdSpi, pData, size); + + while(SDSPI_IsBusy(pSdSpi) == 1); + + if( error == 0 ) { + return 0; + } + else { + TRACE_DEBUG("PB SDSPI_Write\n\r"); + return 1; + } +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +unsigned char SDSPI_WaitDataBusy(SdSpi *pSdSpi) +{ + unsigned char busyData; + + SDSPI_Read(pSdSpi, &busyData, 1); + + if (busyData != 0xff) { + return 1; + } + else { + return 0; + } +} + +/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */ +const uint8_t crc7_syndrome_table[256] = { + 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, + 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, + 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, + 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, + 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, + 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, + 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, + 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, + 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, + 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, + 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, + 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, + 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, + 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, + 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, + 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, + 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, + 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, + 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, + 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, + 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, + 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, + 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, + 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, + 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, + 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, + 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, + 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, + 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, + 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, + 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, + 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 +}; + +// BSS crc functions added +static inline uint8_t crc7_byte(uint8_t crc, uint8_t data) +{ + return crc7_syndrome_table[(crc << 1) ^ data]; +} + +uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len) +{ + while (len--) + crc = crc7_byte(crc, *buffer++); + return crc; +} + +//------------------------------------------------------------------------------ +/// Convert SD MCI command to a SPI mode command token. +/// \param pCmdToken Pointer to the SD command token. +/// \param arg SD command argument +//------------------------------------------------------------------------------ +void SDSPI_MakeCmd(unsigned char *pCmdToken, unsigned int arg) +{ + unsigned char sdCmdNum; + unsigned char crc = 0; + unsigned char crcPrev = 0; + + sdCmdNum = 0x3f & *pCmdToken; + *pCmdToken = sdCmdNum | 0x40; + *(pCmdToken+1) = (arg >> 24) & 0xff; + *(pCmdToken+2) = (arg >> 16) & 0xff; + *(pCmdToken+3) = (arg >> 8) & 0xff; + *(pCmdToken+4) = arg & 0xff; + + crc = crc7(crcPrev, (unsigned char *)(pCmdToken), 5); + + *(pCmdToken+5) = (crc << 1) | 1; +} + +//------------------------------------------------------------------------------ +/// Get response after send SD command. +/// Return 0 if no error, otherwise indicate an error. +/// \param pSdSpi Pointer to the SD SPI instance. +/// \param pCommand Pointer to the SD command +//------------------------------------------------------------------------------ +unsigned char SDSPI_GetCmdResp(SdSpi *pSdSpi, SdSpiCmd *pCommand) +{ + unsigned char resp[8]; // response + unsigned char error; + unsigned int respRetry = 8; //NCR max 8, refer to card datasheet + + memset(resp, 0, 8); + + // Wait for response start bit. + do { + error = SDSPI_Read(pSdSpi, &resp[0], 1); + if (error) { + TRACE_DEBUG("\n\rpb SDSPI_GetCmdResp: 0x%X\n\r", error); + return error; + } + if ((resp[0]&0x80) == 0) { + break; + } + respRetry--; + } while(respRetry > 0); + + switch (pCommand->resType) { + case 1: + *(pCommand->pResp) = resp[0]; + break; + + case 2: + error = SDSPI_Read(pSdSpi, &resp[1], 1); + if (error) { + return error; + } + *(pCommand->pResp) = resp[0] + | (resp[1] << 8); + break; + + // Response 3, get OCR + case 3: + error = SDSPI_Read(pSdSpi, &resp[1], 4); + if (error) { + return error; + } + *(pCommand->pResp) = resp[0] + | (resp[1] << 8) + | (resp[2] << 16) + | (resp[3] << 24); + *(pCommand->pResp+1) = resp[4]; + break; + + case 7: + TRACE_DEBUG("case 7\n\r"); + error = SDSPI_Read(pSdSpi, &resp[1], 4); + if (error) { + return error; + } + *(pCommand->pResp) = resp[0] + | (resp[1] << 8) + | (resp[2] << 16) + | (resp[3] << 24); + *(pCommand->pResp+1) = resp[4]; + break; + + default: + TRACE_DEBUG("PB default\n\r"); + break; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Get response after send data. +/// Return 0 if no error, otherwise indicate an error. +/// \param pSdSpi Pointer to the SD SPI instance. +/// \param pCommand Pointer to the SD command +//------------------------------------------------------------------------------ +unsigned char SDSPI_GetDataResp(SdSpi *pSdSpi, SdSpiCmd *pCommand) +{ + unsigned char resp = 0; // response + unsigned char error; + unsigned int respRetry = 18; //NCR max 8, refer to card datasheet + + // Wait for response start bit. + do { + error = SDSPI_Read(pSdSpi, &resp, 1); + if (error) { + return error; + } + if (((resp & 0x11) == 0x1) || ((resp & 0xf0) == 0)) + break; + + respRetry--; + } while(respRetry > 0); + //TRACE_DEBUG("SDSPI_GetDataResp 0x%X\n\r",resp); + return resp; +} + +// BSS crc16 function added + +const uint16_t crc_itu_t_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +static inline uint16_t crc_itu_t_byte(uint16_t crc, const uint8_t data) +{ + return (crc << 8) ^ crc_itu_t_table[((crc >> 8) ^ data) & 0xff]; +} + +uint16_t crc_itu_t(uint16_t crc, const uint8_t *buffer, size_t len) +{ + while (len--) + crc = crc_itu_t_byte(crc, *buffer++); + return crc; +} + +//------------------------------------------------------------------------------ +/// Starts a SPI master transfer. This is a non blocking function. It will +/// return as soon as the transfer is started. +/// Returns 0 if the transfer has been started successfully; otherwise returns +/// error. +/// \param pSdSpi Pointer to a SdSpi instance. +/// \param pCommand Pointer to the SPI command to execute. +//------------------------------------------------------------------------------ +unsigned char SDSPI_SendCommand(SdSpi *pSdSpi, SdSpiCmd *pCommand) +{ + // BSS AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + unsigned char CmdToken[6]; + unsigned char *pData; + unsigned int blockSize; + unsigned int i; + unsigned char error; + unsigned char dataHeader; + unsigned int dataRetry1 = 100; + unsigned int dataRetry2 = 100; + unsigned char crc[2]; + unsigned char crcPrev = 0; + unsigned char crcPrev2 = 0; + + SANITY_CHECK(pSdSpi); + // BSS SANITY_CHECK(pSpiHw); + SANITY_CHECK(pCommand); + + CmdToken[0] = pCommand->cmd & 0x3F; + pData = pCommand->pData; + blockSize = pCommand->blockSize; + + SDSPI_MakeCmd((unsigned char *)&CmdToken, pCommand->arg); + + // Command is now being executed + pSdSpi->pCommand = pCommand; + pCommand->status = SDSPI_STATUS_PENDING; + + // Send the command + if((pCommand->conTrans == SPI_NEW_TRANSFER) || (blockSize == 0)) { + + for(i = 0; i < 6; i++) { + error = SDSPI_Write(pSdSpi, &CmdToken[i], 1); + if (error) { + TRACE_DEBUG("Error: %d\n\r", error); + return error; + } + } + // Specific for Cmd12() + if ((pCommand->cmd & 0x3F) == 12) { + if( 1 == SDSPI_Wait(pSdSpi, 2) ) { + TRACE_DEBUG("Pb Send command 12\n\r"); + } + } + if (pCommand->pResp) { + error = SDSPI_GetCmdResp(pSdSpi, pCommand); + if (error) { + TRACE_DEBUG("Error: %d\n\r", error); + return error; + } + } + } + + if( (blockSize > 0) && (pCommand->nbBlock == 0) ) { + pCommand->nbBlock = 1; + } + + // For data block operations + while (pCommand->nbBlock > 0) { + + // If data block size is invalid, return error + if (blockSize == 0) { + TRACE_DEBUG("Block Size = 0\n\r"); + return 1; + } + + // DATA transfer from card to host + if (pCommand->isRead) { + do { + SDSPI_Read(pSdSpi, &dataHeader, 1); + dataRetry1 --; + if (dataHeader == SDSPI_START_BLOCK_1) { + break; + } + else if((dataHeader & 0xf0) == 0x00) { + pCommand->status = SDSPI_STATUS_ERROR; + TRACE_DEBUG("Data Error 0x%X!\n\r", dataHeader); + return 1; + } + } while(dataRetry1 > 0); + + if (dataRetry1 == 0) { + TRACE_DEBUG("Timeout dataretry1\n\r"); + return 1; + } + + SDSPI_Read(pSdSpi, pData, blockSize); + + // Specific for Cmd9() + if ((pCommand->cmd & 0x3f) != 0x9) { + + SDSPI_Read(pSdSpi, crc, 2); +#ifdef SDSPI_CRC_ON + // Check data CRC + TRACE_DEBUG("Check Data CRC\n\r"); + crcPrev = 0; + crcPrev2 = 0; + if (crc[0] != ((crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8 ) + || crc[1] != (crc_itu_t(crcPrev2, pData, blockSize) & 0xff)) { + TRACE_ERROR("CRC error 0x%X 0x%X 0x%X\n\r", \ + crc[0], crc[1], crc_itu_t(pData, blockSize)); + return 1; + } +#endif + } + } + + // DATA transfer from host to card + else { + SDSPI_NCS(pSdSpi); + if ((pCommand->conTrans == SPI_CONTINUE_TRANSFER) || ((pCommand->cmd & 0x3f) == 25)) { + dataHeader = SDSPI_START_BLOCK_2; + } + else { + dataHeader = SDSPI_START_BLOCK_1; + } + + crcPrev = 0; + crc[0] = (crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8; + crcPrev2 = 0; + crc[1] = (crc_itu_t(crcPrev2, pData, blockSize) & 0xff); + SDSPI_Write(pSdSpi, &dataHeader, 1); + SDSPI_Write(pSdSpi, pData, blockSize); + SDSPI_Write(pSdSpi, crc, 2); + + // If status bits in data response is not "data accepted", return error + if ((SDSPI_GetDataResp(pSdSpi, pCommand) & 0xe) != 0x4) { + TRACE_ERROR("Write resp error!\n\r"); + return 1; + } + + do { + if (SDSPI_WaitDataBusy(pSdSpi) == 0) { + break; + } + dataRetry2--; + } while(dataRetry2 > 0); + } + pData += blockSize; + pCommand->nbBlock--; + } + + if (pCommand->status == SDSPI_STATUS_PENDING) { + pCommand->status = 0; + } + + //TRACE_DEBUG("end SDSPI_SendCommand\n\r"); + return 0; +} +//! + +//------------------------------------------------------------------------------ +/// The SPI_Handler must be called by the SPI Interrupt Service Routine with the +/// corresponding Spi instance. +/// The SPI_Handler will unlock the Spi semaphore and invoke the upper application +/// callback. +/// \param pSdSpi Pointer to a SdSpi instance. +//------------------------------------------------------------------------------ +void SDSPI_Handler(SdSpi *pSdSpi) +{ + SdSpiCmd *pCommand = pSdSpi->pCommand; + AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + volatile unsigned int spiSr; + + // Read the status register + spiSr = pSpiHw->SPI_SR; + if(spiSr & SPI_SR_RXBUFF/*BSS AT91C_SPI_RXBUFF*/) { + + if (pCommand->status == SDSPI_STATUS_PENDING) { + pCommand->status = 0; + } + // Disable transmitter and receiver + pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Disable the SPI clock + // BSS below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); + PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); + + // Disable buffer complete interrupt + pSpiHw->SPI_IDR = SPI_IDR_RXBUFF | SPI_IDR_ENDTX; // BSS AT91C_SPI_RXBUFF | AT91C_SPI_ENDTX; + + // Release the SPI semaphore + pSdSpi->semaphore++; + } + + // Invoke the callback associated with the current command + if (pCommand && pCommand->callback) { + pCommand->callback(0, pCommand); + } +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the given SPI transfer is complete; otherwise returns 0. +/// \param pCommand Pointer to a SdSpiCmd instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_IsTxComplete(SdSpiCmd *pCommand) +{ + if (pCommand->status != SDSPI_STATUS_PENDING) { + if (pCommand->status != 0){ + TRACE_DEBUG("SPI_IsTxComplete %d\n\r", pCommand->status); + } + return 1; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Close a SPI driver instance and the underlying peripheral. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +void SDSPI_Close(SdSpi *pSdSpi) +{ + AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; + + SANITY_CHECK(pSdSpi); + SANITY_CHECK(pSpiHw); + + // Enable the SPI clock + // BSS below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); + PMC->PMC_PCER0 = (1 << pSdSpi->spiId); + + // Disable the PDC transfer + pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; + + // Enable the SPI + pSpiHw->SPI_CR = SPI_CR_SPIDIS; // BSS AT91C_SPI_SPIDIS; + + // Disable the SPI clock + // BSS below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); + PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); + + // Disable all the interrupts + pSpiHw->SPI_IDR = 0xFFFFFFFF; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the SPI driver is currently busy programming; +/// otherwise returns 0. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_IsBusy(SdSpi *pSdSpi) +{ + if (pSdSpi->semaphore == 0) { + return 1; + } + else { + return 0; + } +} + +//------------------------------------------------------------------------------ +/// Wait several cycles on SPI bus; +/// Returns 0 to indicates no error, otherwise return 1. +/// \param pSdSpi Pointer to a SD SPI driver instance. +/// \param cycles Wait data cycles. +//------------------------------------------------------------------------------ +unsigned char SDSPI_Wait(SdSpi *pSdSpi, unsigned int cycles) +{ + unsigned int i = cycles; + unsigned char data = 0xff; + + for (; i > 0; i--) { + if (SDSPI_Read(pSdSpi, &data, 1)) { + return 1; + } + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Send stop transfer data token; +/// Returns 0 to indicates no error, otherwise return 1. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_StopTranToken(SdSpi *pSdSpi) +{ + unsigned char stopToken = SDSPI_STOP_TRAN; + + TRACE_DEBUG("SDSPI_StopTranToken\n\r"); + return SDSPI_Write(pSdSpi, &stopToken, 1); +} + +//------------------------------------------------------------------------------ +/// Wait, SD card Ncs cycles; +/// Returns 0 to indicates no error, otherwise return 1. +/// \param pSdSpi Pointer to a SD SPI driver instance. +//------------------------------------------------------------------------------ +unsigned char SDSPI_NCS(SdSpi *pSdSpi) +{ + unsigned int i; + unsigned char ncs; + + for(i = 0; i < 15; i++) { + ncs = 0xff; + if (SDSPI_Write(pSdSpi, &ncs, 1)) { + return 1; + } + } + return 0; +} + + diff --git a/src/ersky9x/sdspi.h b/src/ersky9x/sdspi.h new file mode 100644 index 000000000..0f89157e8 --- /dev/null +++ b/src/ersky9x/sdspi.h @@ -0,0 +1,175 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \page "sdspi" +/// +/// !Purpose +/// +/// sdcard spi-bus driver +/// +/// !Usage +/// +/// -# SDSPI_Configure: Initializes the SD Spi structure and the corresponding SPI hardware +/// -# SDSPI_ConfigureCS : Configures the parameters for the device corresponding to the cs +/// -# SDSPI_Read: Read data on SPI data bus +/// -# SDSPI_Write : Write data on SPI data bus +/// -# SDSPI_SendCommand : Starts a SPI master transfer +/// -# SDSPI_StopTranToken : Send stop transfer data token +//------------------------------------------------------------------------------ + +#ifndef SDSPI_H +#define SDSPI_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#ifdef REVA +#include "AT91SAM3S2.h" +#else +#include "AT91SAM3S4.h" +#endif + +//#include +//#include + +/// SPI CSR value +#define SDSPI_CSR(scbr) ( AT91C_SPI_CPOL \ + | AT91C_SPI_BITS_8 \ + | (((scbr)<< 8) & AT91C_SPI_SCBR) \ + | ( (0x08 << 16) & AT91C_SPI_DLYBS)\ + | ( (0x01 << 24) & AT91C_SPI_DLYBCT) ) + +/// Calculates the value of the SCBR field of the Chip Select Register given +/// MCK and SPCK. +#define SPID_CSR_SCBR(mck, spck) ((((mck) / (spck)) << 8) & AT91C_SPI_SCBR) + +/// Calculates the value of the DLYBS field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBS(mck, delay) \ + ((((((delay) * ((mck) / 1000000)) / 1000) + 1) << 16) & AT91C_SPI_DLYBS) + +/// Calculates the value of the DLYBCT field of the Chip Select Register given +/// the delay in ns and MCK. +#define SPID_CSR_DLYBCT(mck, delay) \ + ((((((delay) / 32 * ((mck) / 1000000)) / 1000) + 1) << 24) & AT91C_SPI_DLYBCT) + +#define AT45_CSR(mck, spck) \ + (AT91C_SPI_NCPHA | SPID_CSR_DLYBCT(mck, 250) \ + | SPID_CSR_DLYBS(mck, 250) | SPID_CSR_SCBR(mck, spck)) + + +/// Start new data transfer +#define SPI_NEW_TRANSFER 0 +/// Continue data transfer +#define SPI_CONTINUE_TRANSFER 1 + +/// SD end-of-transfer callback function. +typedef void (*SdSpiCallback)(unsigned char status, void *pCommand); + +//------------------------------------------------------------------------------ +/// SPI Transfer Request prepared by the application upper layer. This structure +/// is sent to the SDSPI_SendCommand function to start the transfer. At the end of +/// the transfer, the callback is invoked by the interrupt handler. +//------------------------------------------------------------------------------ +typedef struct _SdSpiCmd { + + /// Command status. + volatile char status; + /// Command code. + unsigned int cmd; + /// Command argument. + unsigned int arg; + /// Data buffer. + unsigned char *pData; + /// Size of data buffer in bytes. + unsigned short blockSize; + /// Number of blocks to be transfered + unsigned short nbBlock; + /// Indicate if continue to transfer data + unsigned char conTrans; + /// Indicates if the command is a read operation. + unsigned char isRead; + /// Response buffer. + unsigned int *pResp; + /// Size of SD card response in bytes. + unsigned char resType; + /// Optional user-provided callback function. + SdSpiCallback callback; + /// Optional argument to the callback function. + void *pArg; + +} SdSpiCmd; + +/// Above should be put into SD card header file. + +//------------------------------------------------------------------------------ +/// SPI driver structure. Holds the internal state of the SPI driver and +/// prevents parallel access to a SPI peripheral. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to a SPI peripheral. + AT91S_SPI *pSpiHw; + /// SPI peripheral identifier. + unsigned char spiId; + /// Pointer to currently executing command. + SdSpiCmd *pCommand; + /// Mutex. + volatile char semaphore; + +} SdSpi; + +extern void SDSPI_Configure(SdSpi *pSdSpi,AT91PS_SPI pSpiHw,unsigned char spiId); + +extern void SDSPI_SetSpeed(SdSpi *pSdSpi, unsigned int spiSpeed); + +extern unsigned char SDSPI_SendCommand(SdSpi *pSdSpi, SdSpiCmd *pSdSpiCmd); + +extern void SDSPI_Handler(SdSpi *pSdSpi); + +extern unsigned char SDSPI_IsTxComplete(SdSpiCmd *pSdSpiCmd); + +extern unsigned char SDSPI_IsBusy(SdSpi *pSdSpi); + +extern unsigned char SDSPI_NCS(SdSpi *pSdSpi); + +extern void SDSPI_Close(SdSpi *pSdSpi); + +extern void SDSPI_ConfigureCS(SdSpi *pSdSpi, unsigned char cs, unsigned int csr); + +extern unsigned char SDSPI_StopTranToken(SdSpi *pSdSpi); + +extern unsigned char SDSPI_Wait(SdSpi *pSdSpi, unsigned int cycles); + +extern unsigned char SDSPI_WaitDataBusy(SdSpi *pSdSpi); + +#endif +