/* * Integration in Miosix by Terraneo Federico. * Based on code by Martin Thomas to initialize SD cards from LPC2000 */ #include "sd_lpc2000.h" #include "interfaces/bsp.h" #include "LPC213x.h" #include "board_settings.h" //For sdVoltage #include #include //Note: enabling debugging might cause deadlock when using sleep() or reboot() //The bug won't be fixed because debugging is only useful for driver development ///\internal Debug macro, for normal conditions //#define DBG iprintf #define DBG(x,...) do {} while(0) ///\internal Debug macro, for errors only //#define DBGERR iprintf #define DBGERR(x,...) do {} while(0) namespace miosix { ///\internal Type of card (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC static unsigned char cardType=0; /* * Definitions for MMC/SDC command. * A command has the following format: * - 1 bit @ 0 (start bit) * - 1 bit @ 1 (transmission bit) * - 6 bit which identify command index (CMD0..CMD63) * - 32 bit command argument * - 7 bit CRC * - 1 bit @ 1 (end bit) * In addition, ACMDxx are the sequence of two commands, CMD55 and CMDxx * These constants have the following meaninig: * - bit #7 @ 1 indicates that it is an ACMD. send_cmd() will send CMD55, then * clear this bit and send the command with this bit @ 0 (which is start bit) * - bit #6 always @ 1, because it is the transmission bit * - remaining 6 bit represent command index */ #define CMD0 (0x40+0) /* GO_IDLE_STATE */ #define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */ #define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */ #define CMD8 (0x40+8) /* SEND_IF_COND */ #define CMD9 (0x40+9) /* SEND_CSD */ #define CMD10 (0x40+10) /* SEND_CID */ #define CMD12 (0x40+12) /* STOP_TRANSMISSION */ #define CMD13 (0x40+13) /* SEND_STATUS */ #define ACMD13 (0xC0+13) /* SD_STATUS (SDC) */ #define CMD16 (0x40+16) /* SET_BLOCKLEN */ #define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ #define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ #define CMD23 (0x40+23) /* SET_BLOCK_COUNT (MMC) */ #define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */ #define CMD24 (0x40+24) /* WRITE_BLOCK */ #define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ #define CMD42 (0x40+42) /* LOCK_UNLOCK */ #define CMD55 (0x40+55) /* APP_CMD */ #define CMD58 (0x40+58) /* READ_OCR */ // SSPCR0 Bit-Definitions #define CPOL 6 #define CPHA 7 // SSPCR1 Bit-Defintions #define SSE 1 #define MS 2 #define SCR 8 // SSPSR Bit-Definitions #define TNF 1 #define RNE 2 #define BSY 4 #define SPI_SCK_PIN 17 // SCK P0.17 out #define SPI_MISO_PIN 18 // MISO P0.18 in #define SPI_MOSI_PIN 19 // MOSI P0.19 out #define SPI_SS_PIN 20 // CS p0.20 out #define SPI_SCK_FUNCBIT 2 #define SPI_MISO_FUNCBIT 4 #define SPI_MOSI_FUNCBIT 6 #define SPI_SS_FUNCBIT 8 ///\internal Maximum speed 14745600/2=7372800 #define SPI_PRESCALE_MIN 2 ///\internal Select/unselect card #define CS_LOW() IOCLR0 = (1<0x00FF) print_error_code((unsigned char)(value>>8)); else DBGERR("Unknown error 0x%x\n",value); break; } return -1; } /** * \internal * Wait until card is ready */ static unsigned char wait_ready() { unsigned char result; spi_1_send(0xff); for(int i=0;i<460800;i++)//Timeout ~500ms { result=spi_1_send(0xff); if(result==0xff) return 0xff; if(i%500==0) DBG("*\n"); } DBGERR("Error: wait_ready()\n"); return result; } /** * \internal * Send a command to the SD card * \param cmd one among the #define'd commands * \param arg command's argument * \return command's r1 response. If command returns a longer response, the user * can continue reading the response with spi_1_send(0xff) */ static unsigned char send_cmd(unsigned char cmd, unsigned int arg) { unsigned char n, res; if(cmd & 0x80) { // ACMD is the command sequence of CMD55-CMD cmd&=0x7f; res=send_cmd(CMD55,0); if(res>1) return res; } // Select the card and wait for ready CS_HIGH(); CS_LOW(); if(cmd==CMD0) { //wait_ready on CMD0 seems to cause infinite loop spi_1_send(0xff); } else { if(wait_ready()!=0xff) return 0xff; } // Send command spi_1_send(cmd); // Start + Command index spi_1_send((unsigned char)(arg >> 24)); // Argument[31..24] spi_1_send((unsigned char)(arg >> 16)); // Argument[23..16] spi_1_send((unsigned char)(arg >> 8)); // Argument[15..8] spi_1_send((unsigned char)arg); // Argument[7..0] n=0x01; // Dummy CRC + Stop if (cmd==CMD0) n=0x95; // Valid CRC for CMD0(0) if (cmd==CMD8) n=0x87; // Valid CRC for CMD8(0x1AA) spi_1_send(n); // Receive response if (cmd==CMD12) spi_1_send(0xff); // Skip a stuff byte when stop reading n=10; // Wait response, try 10 times do res=spi_1_send(0xff); while ((res & 0x80) && --n); return res; // Return with the response value } /** * \internal * Receive a data packet from the SD card * \param buf data buffer to store received data * \param byte count (must be multiple of 4) * \return true on success, false on failure */ static bool rx_datablock(unsigned char *buf, unsigned int btr) { unsigned char token; for(int i=0;i<0xffff;i++) { token=spi_1_send(0xff); if(token!=0xff) break; } if(token!=0xfe) return false; // If not valid data token, retutn error do { // Receive the data block into buffer *buf=spi_1_send(0xff); buf++; *buf=spi_1_send(0xff); buf++; *buf=spi_1_send(0xff); buf++; *buf=spi_1_send(0xff); buf++; } while(btr-=4); spi_1_send(0xff); // Discard CRC spi_1_send(0xff); return true; // Return success } /** * \internal * Send a data packet to the SD card * \param buf 512 byte data block to be transmitted * \param token data start/stop token * \return true on success, false on failure */ static bool tx_datablock (const unsigned char *buf, unsigned char token) { unsigned char resp; if(wait_ready()!=0xff) return false; spi_1_send(token); // Xmit data token if (token!=0xfd) { // Is data token for(int i=0;i<256;i++) { // Xmit the 512 byte data block spi_1_send(*buf); buf++; spi_1_send(*buf); buf++; } spi_1_send(0xff); // CRC (Dummy) spi_1_send(0xff); resp=spi_1_send(0xff); // Receive data response if((resp & 0x1f)!=0x05) // If not accepted, return error return false; } return true; } // // class SPISDDriver // intrusive_ref_ptr SPISDDriver::instance() { static FastMutex m; static intrusive_ref_ptr instance; Lock l(m); if(!instance) instance=new SPISDDriver(); return instance; } ssize_t SPISDDriver::readBlock(void* buffer, size_t size, off_t where) { if(where % 512 || size % 512) return -EFAULT; unsigned int lba=where/512; unsigned int nSectors=size/512; unsigned char *buf=reinterpret_cast(buffer); Lock l(mutex); DBG("SPISDDriver::readBlock(): nSectors=%d\n",nSectors); if(!(cardType & 8)) lba*=512; // Convert to byte address if needed unsigned char result; if(nSectors==1) { // Single block read result=send_cmd(CMD17,lba); // READ_SINGLE_BLOCK if(result!=0) { print_error_code(result); CS_HIGH(); return -EBADF; } if(rx_datablock(buf,512)==false) { DBGERR("Block read error\n"); CS_HIGH(); return -EBADF; } } else { // Multiple block read //DBGERR("Mbr\n");//debug only result=send_cmd(CMD18,lba); // READ_MULTIPLE_BLOCK if(result!=0) { print_error_code(result); CS_HIGH(); return -EBADF; } do { if(!rx_datablock(buf,512)) break; buf+=512; } while(--nSectors); send_cmd(CMD12,0); // STOP_TRANSMISSION if(nSectors!=0) { DBGERR("Multiple block read error\n"); CS_HIGH(); return -EBADF; } } CS_HIGH(); return size; } ssize_t SPISDDriver::writeBlock(const void* buffer, size_t size, off_t where) { if(where % 512 || size % 512) return -EFAULT; unsigned int lba=where/512; unsigned int nSectors=size/512; const unsigned char *buf=reinterpret_cast(buffer); Lock l(mutex); DBG("SPISDDriver::writeBlock(): nSectors=%d\n",nSectors); if(!(cardType & 8)) lba*=512; // Convert to byte address if needed unsigned char result; if(nSectors==1) { // Single block write result=send_cmd(CMD24,lba); // WRITE_BLOCK if(result!=0) { print_error_code(result); CS_HIGH(); return -EBADF; } if(tx_datablock(buf,0xfe)==false) // WRITE_BLOCK { DBGERR("Block write error\n"); CS_HIGH(); return -EBADF; } } else { // Multiple block write //DBGERR("Mbw\n");//debug only if(cardType & 6) send_cmd(ACMD23,nSectors);//Only if it is SD card result=send_cmd(CMD25,lba); // WRITE_MULTIPLE_BLOCK if(result!=0) { print_error_code(result); CS_HIGH(); return -EBADF; } do { if(!tx_datablock(buf,0xfc)) break; buf+=512; } while(--nSectors); if(!tx_datablock(0,0xfd)) // STOP_TRAN token { DBGERR("Multiple block write error\n"); CS_HIGH(); return -EBADF; } } CS_HIGH(); return size; } int SPISDDriver::ioctl(int cmd, void* arg) { DBG("SPISDDriver::ioctl()\n"); if(cmd!=IOCTL_SYNC) return -ENOTTY; Lock l(mutex); CS_LOW(); unsigned char result=wait_ready(); CS_HIGH(); if(result==0xff) return 0; else return -EFAULT; } SPISDDriver::SPISDDriver() : Device(Device::BLOCK) { const int MAX_RETRY=20;//Maximum command retry before failing spi_1_init(); /* init at low speed */ unsigned char resp; int i; for(i=0;i