summaryrefslogtreecommitdiffstats
path: root/main/common/sd.c
diff options
context:
space:
mode:
authorAmar Takhar <amar@rtems.org>2015-04-16 15:26:21 -0400
committerAmar Takhar <amar@rtems.org>2015-04-16 15:26:21 -0400
commit87db514f2dfff5ad67863a30f075b718706de346 (patch)
tree31edc1b1700de9f3d4825822534928f8a213d53f /main/common/sd.c
downloadumon-87db514f2dfff5ad67863a30f075b718706de346.tar.bz2
Initial commit of the umon repository.
Prior to this three changes were made: * Remove umon_ prefix from parent directories. * Collapse main/target/ into main/ * Remove ports/template/flashtest.scr.ucon script.
Diffstat (limited to 'main/common/sd.c')
-rw-r--r--main/common/sd.c561
1 files changed, 561 insertions, 0 deletions
diff --git a/main/common/sd.c b/main/common/sd.c
new file mode 100644
index 0000000..6d2857d
--- /dev/null
+++ b/main/common/sd.c
@@ -0,0 +1,561 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2013 Alcatel-Lucent
+ *
+ * Alcatel Lucent licenses this file to You under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. A copy of the License is contained the
+ * file LICENSE at the top level of this repository.
+ * You may also obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **************************************************************************
+ *
+ * sd.c:
+ *
+ * This code is the user interface portion of the sd (compact flash)
+ * command for uMon.
+ * This command is intended to be the "interface" portion of some
+ * other command (for example "fatfs"). Refer to the discussion in
+ * fatfs.c for more details.
+ *
+ * Original author: Ed Sutter (ed.sutter@alcatel-lucent.com)
+ *
+ */
+
+#include "config.h"
+#if INCLUDE_SD
+#include "stddefs.h"
+#include "genlib.h"
+#include "cli.h"
+#include "timer.h"
+#include "tfs.h"
+#include "tfsprivate.h"
+#include "sd.h"
+
+struct sdinfo sdInfoTbl[SD_DEVTOT];
+char sdVerbose;
+static int sdInum; /* Interface number: to support multiple SD interfaces.
+ * Typically this will always be zero.
+ */
+
+
+static struct sdcmd sdCardCmdTbl[] = {
+ { GO_IDLE_STATE, R1_RLEN, R1 },
+ { SEND_OP_COND, R1_RLEN, R1 },
+ { SWITCH_FUNC, R1_RLEN, R1 },
+ { SEND_IF_COND, R7_RLEN, R7 },
+ { SEND_CSD, R1_RLEN, R1 },
+ { SEND_CID, R1_RLEN, R1 },
+ { STOP_TRANSMISSION, R1_RLEN, R1B },
+ { SEND_STATUS, R2_RLEN, R2 },
+ { SET_BLOCKLEN, R1_RLEN, R1 },
+ { READ_SINGLE_BLK, R1_RLEN, R1 },
+ { READ_MULTIPLE_BLK, R1_RLEN, R1 },
+ { WRITE_BLK, R1_RLEN, R1B },
+ { WRITE_MULTIPLE_BLK, R1_RLEN, R1B },
+ { PROGRAM_CSD, R1_RLEN, R1 },
+ { SET_WRITE_PROT, R1_RLEN, R1B },
+ { CLR_WRITE_PROT, R1_RLEN, R1B },
+ { SEND_WRITE_PROT, R1_RLEN, R1 },
+ { ERASE_WR_BLK_START_ADDR, R1_RLEN, R1 },
+ { ERASE_WR_BLK_END_ADDR, R1_RLEN, R1 },
+ { ERASE, R1_RLEN, R1B },
+ { LOCK_UNLOCK, R1_RLEN, R1 },
+ { APP_CMD, R1_RLEN, R1 },
+ { GEN_CMD, R1_RLEN, R1 },
+ { READ_OCR, R3_RLEN, R3 },
+ { CRC_ON_OFF, R1_RLEN, R1 },
+ { SD_SEND_OP_COND, R1_RLEN, R1 },
+ { -1,-1,-1 }
+};
+
+char *SdHelp[] = {
+ "Secure Digital Flash Interface",
+ "[options] {operation} [args]...",
+#if INCLUDE_VERBOSEHELP
+ "",
+ "Options:",
+ " -i ## interface # (default is 0)",
+ " -v additive verbosity",
+ "",
+ "Operations:",
+ " init [prefix]",
+ " cmd {cmdnum} [arg]",
+ " read {dest} {blk} {blktot}",
+ " write {blk} {dest} {blktot}",
+#endif
+ 0
+};
+
+int
+SdCmd(int argc, char *argv[])
+{
+ char *cmd, *buf, *prefix, varname[16];
+ int opt, verbose, sdret, blknum, blkcnt;
+
+ verbose = 0;
+ while ((opt=getopt(argc,argv,"i:v")) != -1) {
+ switch(opt) {
+ case 'i':
+ sdInum = atoi(optarg); /* sticky */
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ return(CMD_PARAM_ERROR);
+ }
+ }
+
+ if (argc < optind + 1)
+ return(CMD_PARAM_ERROR);
+
+ cmd = argv[optind];
+
+ if (sdInum >= SD_DEVTOT) {
+ printf("Configured to support %d SD interface%s\n",
+ SD_DEVTOT,SD_DEVTOT == 1 ? "" : "s");
+ return(CMD_FAILURE);
+ }
+
+ if (sdInstalled(sdInum) == 0) {
+ printf("SDCard not installed\n");
+ return(CMD_FAILURE);
+ }
+
+ if (strcmp(cmd,"init") == 0) {
+ sdret = sdInit(sdInum, verbose);
+ if (sdret < 0) {
+ printf("sdInit returned %d\n",sdret);
+ return(CMD_FAILURE);
+ }
+
+ // If prefix is specified, then load shell variables:
+ if (argc == optind+2) {
+ prefix = argv[optind+1];
+ if (strlen(prefix)+4 > sizeof(varname)) {
+ printf("prefix %s too long\n",prefix);
+ return(CMD_PARAM_ERROR);
+ }
+
+ sprintf(varname,"%s_RD",prefix);
+ shell_sprintf(varname,"0x%lx",(long)sdRead);
+
+ sprintf(varname,"%s_WR",prefix);
+ shell_sprintf(varname,"0x%lx",(long)sdWrite);
+ }
+
+ shell_sprintf("SD_BLKSIZE","0x%lx",SD_BLKSIZE);
+ }
+ else if (strcmp(cmd,"cmd") == 0) {
+ ulong cmdarg;
+ uchar resp[8];
+ int rtot, i, cmdnum;
+
+ cmdarg = 0;
+ memset((char *)resp,0xff,sizeof(resp));
+
+ if ((argc != (optind+2)) && (argc != (optind+3)))
+ return(CMD_PARAM_ERROR);
+
+ cmdnum = (uchar)strtoul(argv[optind+1],0,0);
+ if (argc == optind+3)
+ cmdarg = strtoul(argv[optind+2],0,0);
+ sdret = sdCardCmd(sdInum, cmdnum , cmdarg ,resp);
+ if (sdret < 0) {
+ printf("sdCardCmd returned %d\n",sdret);
+ return(CMD_FAILURE);
+ }
+
+ rtot = sdCmdrlen(cmdnum);
+
+ printf("CMD_%d resp: ",cmdnum);
+ for(i=0;i<rtot;i++)
+ printf("%02x ",resp[i]);
+ printf("\n");
+ }
+ else if (strcmp(cmd,"read") == 0) {
+ if (argc != optind+4)
+ return(CMD_PARAM_ERROR);
+
+ buf = (char *)strtoul(argv[optind+1],0,0);
+ blknum = strtoul(argv[optind+2],0,0);
+ blkcnt = strtoul(argv[optind+3],0,0);
+
+ sdret = sdRead(sdInum,buf,blknum,blkcnt);
+ if (sdret < 0) {
+ printf("sdRead returned %d\n",sdret);
+ return(CMD_FAILURE);
+ }
+ }
+ else if (strcmp(cmd,"write") == 0) {
+ if (argc != optind+4)
+ return(CMD_PARAM_ERROR);
+ buf = (char *)strtoul(argv[optind+1],0,0);
+ blknum = strtoul(argv[optind+2],0,0);
+ blkcnt = strtoul(argv[optind+3],0,0);
+
+ sdret = sdWrite(sdInum,buf,blknum,blkcnt);
+ if (sdret < 0) {
+ printf("sdWrite returned %d\n",sdret);
+ return(CMD_FAILURE);
+ }
+ }
+ else {
+ printf("sd op <%s> not found\n",cmd);
+ return(CMD_FAILURE);
+ }
+
+ return(CMD_SUCCESS);
+}
+
+/* sdCmdrlen():
+ * Given the command number, return the response length.
+ */
+int
+sdCmdrlen(int cmd)
+{
+ struct sdcmd *sdp = sdCardCmdTbl;
+
+ while(sdp->cmd != -1) {
+ if (cmd == sdp->cmd)
+ return(sdp->rlen);
+ sdp++;
+ }
+ return(-1);
+}
+
+/* sdCmdrtype():
+ * Given the command number, return the response type.
+ */
+int
+sdCmdrtype(int cmd)
+{
+ struct sdcmd *sdp = sdCardCmdTbl;
+
+ while(sdp->cmd != -1) {
+ if (cmd == sdp->cmd)
+ return(sdp->rtype);
+ sdp++;
+ }
+ return(-1);
+}
+
+static char *months[] = {
+ "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+};
+
+void
+sdShowCID(uchar *cid)
+{
+ int year, mon;
+
+ printf(" Manufacturer ID: x%02x\n", cid[0]);
+ printf(" OEM/Application ID: x%02x%02x\n", cid[1],cid[2]);
+ printf(" Product name: %c%c%c%c%c\n",
+ cid[3],cid[4],cid[5],cid[6],cid[7]);
+ printf(" Product rev: x%02x\n", cid[8]);
+ printf(" Product serialno: x%02x%02x%02x%02x\n",
+ cid[9],cid[10],cid[11],cid[12]);
+
+ year = (((cid[13] & 0x0f) << 4) | ((cid[14] & 0xf0) >> 4));
+ mon = (cid[14] & 0x0f);
+ if ((mon < 1) || (mon > 12))
+ mon = 0;
+ printf(" Manufactured: %s/%d\n",months[mon],2000+year);
+}
+
+void
+sdShowCSD(uchar *csd)
+{
+ uchar csdver;
+ long long capacity;
+ int mult, csize, csm, rbl;
+ int blocknr, blocklen, i;
+
+ printf(" CSD Response: ");
+ for(i=0;i<16;i++)
+ printf("%02x ",csd[i]);
+ printf("\n");
+
+ mult = csize = csm = rbl = 0;
+
+ if ((csd[0] & 0xc0) == 0)
+ csdver = 1;
+ else if ((csd[0] & 0xc0) == 0x40)
+ csdver = 2;
+ else {
+ printf("Invalid CSD structure type\n");
+ return;
+ }
+
+ printf(" CSD version %d.0\n",csdver);
+ if (csdver == 1) {
+ rbl = csd[5] & 0x0f;
+ csize = ((csd[8] & 0xC0) >> 6);
+ csize |= (csd[7] << 2);
+ csize |= ((csd[6] & 0x03) << 10);
+ csm = (csd[9] & 0x03);
+ csm <<= 1;
+ csm |= ((csd[10] & 0x80) >> 7);
+
+ mult = (1 << (csm+2));
+ blocknr = (csize+1)*mult;
+ blocklen = (1 << rbl);
+ capacity = (long long)((long long)blocknr * (long long)blocklen);
+ //printf(" (csm=%d, csize=%d, rbl=%d, mult=%d, blknr=%d, blkln=%d)\n",
+ // csm, csize, rbl, mult, blocknr, blocklen);
+ }
+ else if (csdver == 2) {
+ rbl = csd[5] & 0x0f;
+ csize = (csd[7] & 0x3f) << 16;
+ csize |= (csd[8] << 8);
+ csize |= csd[9];
+ capacity = (long long)((long long)(csize + 1) * 512LL * 1024LL);
+ }
+ else {
+ printf(" Unrecognized CSD version.\n");
+ return;
+ }
+ printf(" Card capacity: %lld bytes\n",capacity);
+}
+
+/* sdGenericStartup():
+ * Called by the interface-specific sdInit() code to do the generic
+ * portion of the SD-Card initialization.
+ *
+ * Refer to the flowchart shown in figure 7.2 of the Simplified
+ * Physical Layer Specification for an overview of the initialization.
+ *
+ * The card wakes up in SD bus mode, so the first thing we need to d
+ * here (after the very basic initialization of the SPI pin config) is
+ * to get the card into SPI mode.
+ * By default, the card assumes CRC checking is disabled. The field used
+ * to hold the CRC is still part of the protocol, but it is ignored. If
+ * needed, the master can turn it on with CMD59.
+ */
+int
+sdGenericStartup(int interface)
+{
+ ulong hcs;
+ uchar resp[16], cid[16], csd[16];
+ int retry, rc, version;
+ struct elapsed_tmr tmr;
+ int check_pattern_retry = 2;
+
+ sdInfoTbl[interface].initialized = 0;
+
+ rc = 0;
+ check_pattern_retry = 2;
+
+ // Start with at least 74 clocks to powerup the card...
+ sdPowerup(10);
+
+ // Put card in SPI mode:
+
+ // This command always seems to fail on the first try after a
+ // powerup, so give it a few attempts...
+ for(retry=0;retry<3;retry++) {
+ if ((rc = sdCardCmd(interface,GO_IDLE_STATE,0,resp)) != -1)
+ break;
+ }
+
+ if (retry == 3) {
+ printf("\nSD GO_IDLE_STATE failed, card installed?\n");
+ return(-1);
+ }
+
+ // According to Physical Layer Simplified Spec (PLSS), it is mandatory
+ // for the host compliant to Version 2.00 to send CMD8 (SEND_IF_COND).
+ // If CMD8 is not recognized (illegal command), then we can assume that
+ // the card is version 1.00.
+ // The argument sent with the command is defined in section 4.3.13
+ // of the PLSS.
+ // The spec says that if the check-patter test fails, to retry...
+ do {
+ if ((rc = sdCardCmd(interface,SEND_IF_COND,SEND_IF_COND_ARG,resp)) == -1)
+ return(-1);
+
+ if (resp[0] & R1_ILLEGAL_CMD) {
+ version = VERSION_10;
+ hcs = 0;
+ break; // Check pattern only applies to V2 and later
+ }
+ else {
+ // The card should echo back the VHS and check pattern...
+ if (resp[3] != VHS27_36) {
+ if (sdVerbose & 2)
+ printf("SDCARD not 3.3v compliant!\n");
+ return(-1);
+ }
+ if (resp[4] != CHECK_PATTERN) {
+ if (--check_pattern_retry <= 0) {
+ if (sdVerbose & 2)
+ printf("SDCARD check-pattern failed.\n");
+ return(-1);
+ }
+ }
+ version = VERSION_20;
+ hcs = HCS;
+ }
+ } while(resp[4] != CHECK_PATTERN);
+
+ // Read OCR to make sure the card will run at 3.3v...
+ if ((rc = sdCardCmd(interface,READ_OCR,hcs,resp)) == -1)
+ return(-1);
+
+ if ((resp[2] & MSK_OCR_33) != MSK_OCR_33) {
+ if (sdVerbose & 2)
+ printf("SDCARD: OCR_33 failed, card isn't 3.3v capable\n");
+ return(-1);
+ }
+
+ // Wait for card to complete initialization:
+ startElapsedTimer(&tmr,1000);
+ while(1) {
+ if ((rc = sdCardCmd(interface,SD_SEND_OP_COND,HCS,resp)) == -1)
+ return(-1);
+ if ((resp[0] & R1_IDLE) == 0)
+ break;
+ if(msecElapsed(&tmr)) {
+ printf("SDCARD: gaveup waiting for init to complete.\n");
+ return(-1);
+ }
+ }
+
+ if (version == VERSION_20) {
+ // Get CCS...
+ if ((rc = sdCardCmd(interface,READ_OCR,0,resp)) == -1)
+ return(-1);
+ }
+ else {
+ if ((rc = sdCardCmd(interface,SET_BLOCKLEN,SD_BLKSIZE,resp)) == -1)
+ return(-1);
+ }
+
+ if (sdVerbose) {
+ printf("SD/SPI Initialized (version %s)\n",
+ version == VERSION_10 ? "1.0" : ">=2.0");
+ }
+
+ sdInfoTbl[interface].cardversion = version;
+
+ sdReadCxD(interface,cid,SEND_CID);
+ if (sdVerbose)
+ sdShowCID(cid);
+ sdReadCxD(interface,csd,SEND_CSD);
+ if (sdVerbose)
+ sdShowCSD(csd);
+
+ if ((csd[0] & 0xc0) == 0x00)
+ sdInfoTbl[interface].highcapacity = 0;
+ else
+ sdInfoTbl[interface].highcapacity = 1;
+
+ sdInfoTbl[interface].initialized = 1;
+ return(0);
+}
+
+/* Got this crc7 code off the web...
+ * The original text claimed "use-as-you-wish"...
+ */
+#define POLYNOM (0x9) // polynomical value to XOR when 1 pops out.
+
+static unsigned char
+Encode(uchar seed, uchar input, uchar depth)
+{
+ uchar regval, count, cc;
+
+ regval = seed;
+ cc = input;
+
+ for (count = depth ; count-- ; cc <<= 1) {
+ regval = (regval << 1) + ((cc & 0x80) ? 1 : 0);
+ if (regval & 0x80)
+ regval ^= POLYNOM;
+ }
+ return(regval & 0x7f); // return lower 7 bits of CRC as value to use.
+}
+
+uchar
+crc7(uchar seed, uchar *buf, int len)
+{
+ int i;
+ uchar crc;
+
+ crc = seed;
+ for (i = 0; i < len; i++)
+ crc = Encode(crc, buf[i], 8);
+
+ crc = Encode(crc,0,7);
+ crc = (crc << 1) + 1;
+ return(crc);
+}
+
+#ifdef INCLUDE_SD_DUMMY_FUNCS
+/* This code is included here just for simulating the SD
+ * interface (temporarily if a real one isn't ready. In a real system,
+ * the INCLUDE_SD_DUMMY_FUNCS definition would be off.
+ */
+
+int
+sdInit(int interface, int verbose)
+{
+ if (interface != 0)
+ return(-1);
+
+ return(0);
+}
+
+int
+sdRead(int interface, char *buf, int blk, int blkcnt)
+{
+ char *from;
+ int size;
+
+ if (interface != 0)
+ return(-1);
+
+ from = (char *)(blk * SD_BLKSIZE);
+ size = blkcnt * SD_BLKSIZE;
+ memcpy(buf,from,size);
+ return(0);
+}
+
+int
+sdWrite(int interface, char *buf, int blk, int blkcnt)
+{
+ char *to;
+ int size;
+
+ if (interface != 0)
+ return(-1);
+
+ to = (char *)(blk * SD_BLKSIZE);
+ size = blkcnt * SD_BLKSIZE;
+ memcpy(to,buf,size);
+ return(0);
+}
+
+/* sdInstalled():
+ * Return 1 if installed, 0 if not installed or -1 if
+ * the hardware can't detect installation.
+ */
+int
+sdInstalled(int interface)
+{
+ return(-1);
+}
+
+#endif
+
+#endif