/* * Copyright (c) 2015 Jarielle Catbagan * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.apache.org/licenses/LICENSE-2.0 */ #include "genlib.h" #include "cli.h" #include "stddefs.h" #include "am335x.h" #include "am335x_mmc.h" uint16_t mmcrca; int mmcInum; char *mmcHelp[] = { "MultiMediaCard Interface", "[options] {operation} [args]...", #if INCLUDE_VERBOSEHELP "", "Options:", " -i ## interface # (default is 0)", " -v additive verbosity", "", "Operations:", " init", " read {dest} {blk} {blktot}", " write {source} {blk} {blktot}", #endif /* INCLUDE_VERBOSEHELP */ 0 }; int mmccmd(uint32_t cmd, uint32_t arg, uint32_t resp[4]) { /* Clear the SD_STAT register for proper update of status bits after CMD invocation */ MMC1_REG(SD_STAT) = 0xFFFFFFFF; MMC1_REG(SD_ARG) = arg; MMC1_REG(SD_CMD) = cmd; /* CMDx complete? */ while (!(MMC1_REG(SD_STAT) & (SD_STAT_CC | SD_STAT_ERRI))); resp[0] = MMC1_REG(SD_RSP10); resp[1] = MMC1_REG(SD_RSP32); resp[2] = MMC1_REG(SD_RSP54); resp[3] = MMC1_REG(SD_RSP76); /* CMDx error? */ if (MMC1_REG(SD_STAT) & SD_STAT_ERRI) return(-1); else return(0); } int mmc(int argc, char *argv[]) { char *cmd, *buf; int opt, verbose, mmcret, blknum, blkcnt; verbose = 0; while ((opt = getopt(argc, argv, "i:v")) != -1) { switch (opt) { case 'i': mmcInum = atoi(optarg); break; case 'v': verbose++; break; default: return(CMD_PARAM_ERROR); } } if (argc < optind + 1) return(CMD_PARAM_ERROR); cmd = argv[optind]; if (mmcInstalled(mmcInum) == 0) { printf("MMC not installed\n"); return(CMD_FAILURE); } if (strcmp(cmd, "init") == 0) { mmcret = mmcInit(mmcInum, verbose); if(mmcret < 0) { printf("mmcInit returned %d\n", mmcret); return(CMD_FAILURE); } } 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); mmcret = mmcRead(mmcInum, buf, blknum, blkcnt); if (mmcret < 0) { printf("mmcRead returned %d\n", mmcret); 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); mmcret = mmcWrite(mmcInum, buf, blknum, blkcnt); if (mmcret < 0) { printf("mmcWrite returned %d\n", mmcret); return(CMD_FAILURE); } } else { printf("mmc op <%s> not found\n", cmd); return(CMD_FAILURE); } return(CMD_SUCCESS); } int mmcInit(int interface, int verbose) { uint32_t cmd, arg, resp[4]; /* Reset the MMC1 Controller */ MMC1_REG(SD_SYSCONFIG) = SD_SYSCONFIG_SOFTRESET; while (!(MMC1_REG(SD_SYSSTATUS) & SD_SYSSTATUS_RESETDONE)); /* Reset the command and data lines */ MMC1_REG(SD_SYSCTL) |= SD_SYSCTL_SRA; while (MMC1_REG(SD_SYSCTL) & SD_SYSCTL_SRA); /* Configure the MMC1 controller capabilities to enable 3.0 V operating voltage */ MMC1_REG(SD_CAPA) |= SD_CAPA_VS30; /* Configure SD_IE register to update certain status bits in SD_STAT */ MMC1_REG(SD_IE) = SD_IE_BADA_ENABLE | SD_IE_CERR_ENABLE | SD_IE_ACE_ENABLE | SD_IE_DEB_ENABLE | SD_IE_DCRC_ENABLE | SD_IE_DTO_ENABLE | SD_IE_CIE_ENABLE | SD_IE_CEB_ENABLE | SD_IE_CCRC_ENABLE | SD_IE_CIRQ_ENABLE | SD_IE_CREM_ENABLE | SD_IE_CINS_ENABLE | SD_IE_BRR_ENABLE | SD_IE_BWR_ENABLE | SD_IE_TC_ENABLE | SD_IE_CC_ENABLE; /* Configure the operating voltage to 3.0 V */ MMC1_REG(SD_HCTL) &= ~(SD_HCTL_SDVS); MMC1_REG(SD_HCTL) |= SD_HCTL_SDVS_VS30; /* Turn on the bus */ MMC1_REG(SD_HCTL) |= SD_HCTL_SDBP; while (!(MMC1_REG(SD_HCTL) & SD_HCTL_SDBP)); /* Enable the internal clock */ MMC1_REG(SD_SYSCTL) |= SD_SYSCTL_ICE; /* Configure Clock Frequency Select to 100 KHz */ MMC1_REG(SD_SYSCTL) = (MMC1_REG(SD_SYSCTL) & ~SD_SYSCTL_CLKD) | (960 << 6); /* Wait for clock to stabilize */ while (!(MMC1_REG(SD_SYSCTL) & SD_SYSCTL_ICS)); /* Configure SD_SYSCONFIG */ MMC1_REG(SD_SYSCONFIG) &= ~(SD_SYSCONFIG_CLOCKACTIVITY | SD_SYSCONFIG_SIDLEMODE); MMC1_REG(SD_SYSCONFIG) |= SD_SYSCONFIG_SIDLEMODE_WKUP | SD_SYSCONFIG_ENAWAKEUP_ENABLE | SD_SYSCONFIG_AUTOIDLE_AUTOGATE; /* Enable the clock to the eMMC */ MMC1_REG(SD_SYSCTL) |= SD_SYSCTL_CEN; /* Perform the Initialization Stream as specified in the AM335x TRM, Section 18.3.3.2 "Card Detection, Identification, and Selection" */ MMC1_REG(SD_CON) |= SD_CON_INIT; /* Clear the SD_STAT register */ MMC1_REG(SD_STAT) = 0xFFFFFFFF; MMC1_REG(SD_ARG) = 0x00000000; MMC1_REG(SD_CMD) = 0x00000000; while (!(MMC1_REG(SD_STAT) & SD_STAT_CC)); /* Clear CC flag in SD_STAT */ MMC1_REG(SD_STAT) |= SD_STAT_CC; MMC1_REG(SD_CON) &= ~SD_CON_INIT; /* Clear the SD_STAT register */ MMC1_REG(SD_STAT) = 0xFFFFFFFF; /* Enable open-drain mode until we enter Stand-by State as illustrated in the JEDEC JESD84-A43 Embedded MultiMediaCard Product Standard specification, Table 5 */ MMC1_REG(SD_CON) |= SD_CON_OD; /* Send CMD0/GO_IDLE_STATE to reset the eMMC on MMC1 interface */ arg = 0x00000000; cmd = SD_CMD_CMD0_GO_IDLE_STATE | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_DISABLE | SD_CMD_CCCE_DISABLE | SD_CMD_RSP_TYPE_NO_RESPONSE; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Send CMD1 and poll busy bit in response */ do { arg = 0x40FF8000; cmd = SD_CMD_CMD1_SEND_OP_COND | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_DISABLE | SD_CMD_CCCE_DISABLE | SD_CMD_RSP_TYPE_R3; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while (!(MMC1_REG(SD_RSP10) & 0x80000000)); /* Send CMD2, i.e. ALL_SEND_CID */ arg = 0x00000000; cmd = SD_CMD_CMD2_ALL_SEND_CID | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_DISABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R2; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Set RCA of eMMC */ mmcrca = 0x3A3A; /* Send CMD3 to set the relative card address (RCA) of the eMMC */ arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD3_SET_RELATIVE_ADDR | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Wait for the eMMC to enter Stand-by State */ do { /* Send CMD13 to get the status of the MMC */ arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY); /* Disable open-drain mode */ MMC1_REG(SD_CON) &= ~SD_CON_OD; /* Send CMD7 to put the eMMC into Transfer State */ arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD7_SELECT_DESELECT_CARD | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Wait for eMMC to enter Transfer State */ do { /* Send CMD13 to get the status of the eMMC */ arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER); /* Send CMD6 to change bus-width to 8-bits */ arg = (3 << 24) | (183 << 16) | (2 << 8); cmd = SD_CMD_CMD6_SWITCH | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1B; if (mmccmd(cmd, arg, resp) == -1) return(-1); while (!(MMC1_REG(SD_STAT) & SD_STAT_TC)); /* Wait while CMD6 is still in effect, i.e. while eMMC is not in Transfer State */ do { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER); /* Configure the MMC1 controller to use an 8-bit data width */ MMC1_REG(SD_CON) |= SD_CON_DW8_8BIT; /* Send CMD6 to change to high-speed mode */ arg = 0x03B90100; cmd = SD_CMD_CMD6_SWITCH | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1B; if (mmccmd(cmd, arg, resp) == -1) return(-1); while (!(MMC1_REG(SD_STAT) & SD_STAT_TC)); /* Wait while CMD6 is still in effect, i.e. while eMMC is not in Transfer State */ do { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER); /* Change the clock frequency to 48 MHz and set the DTO to the maximum value setting */ MMC1_REG(SD_SYSCTL) &= ~SD_SYSCTL_DTO; MMC1_REG(SD_SYSCTL) |= SD_SYSCTL_DTO_TCF_2_27; MMC1_REG(SD_SYSCTL) = (MMC1_REG(SD_SYSCTL) & ~SD_SYSCTL_CLKD) | (2 << 6); /* Wait for clock to stabilize */ while ((MMC1_REG(SD_SYSCTL) & SD_SYSCTL_ICS) != SD_SYSCTL_ICS); /* Put the eMMC into Stand-by State */ arg = 0x00000000; cmd = SD_CMD_CMD7_SELECT_DESELECT_CARD | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_DISABLE | SD_CMD_CCCE_DISABLE | SD_CMD_RSP_TYPE_NO_RESPONSE; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Wait for the eMMC to enter Stand-by State */ do { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY); return(0); } int mmcRead(int interface, char *buf, int blknum, int blkcnt) { uint32_t cmd, arg, resp[4]; uint32_t *wordptr = (uint32_t *) buf; int byteindex; /* Get the SD card's status via CMD13 */ arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Ensure that the card is in Transfer State before proceeding */ if ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER) { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD7_SELECT_DESELECT_CARD | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1B; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Wait for the SD card to enter Transfer State */ do { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER); } /* Set the block length and the number of blocks to read */ MMC1_REG(SD_BLK) = 0x200 | (blkcnt << 16); /* Read multiple blocks via CMD18 */ arg = blknum; cmd = SD_CMD_CMD18_READ_MULTIPLE_BLOCK | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1 | SD_CMD_MSBS_MULTIPLE | SD_CMD_DDIR_READ | SD_CMD_ACEN_CMD12_ENABLE | SD_CMD_BCE_ENABLE; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Check the data buffer to see if there is data to be read */ do { while (!(MMC1_REG(SD_STAT) & SD_STAT_BRR)); /* Clear the BRR status bit in SD_STAT */ MMC1_REG(SD_STAT) |= SD_STAT_BRR; for (byteindex = 0; byteindex < (0x200 / 4); byteindex++) { *wordptr = MMC1_REG(SD_DATA); wordptr++; } } while (!(MMC1_REG(SD_STAT) & SD_STAT_TC)); /* Put the eMMC into Stand-by State */ arg = 0; cmd = SD_CMD_CMD7_SELECT_DESELECT_CARD | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_DISABLE | SD_CMD_CCCE_DISABLE | SD_CMD_RSP_TYPE_NO_RESPONSE; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Wait for the eMMC to enter Stand-by State */ do { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY); return(0); } int mmcWrite(int interface, char *buf, int blknum, int blkcnt) { uint32_t cmd, arg, resp[4]; uint32_t *wordptr = (uint32_t *) buf; int byteindex; /* Get the eMMC status by sending CMD13 */ arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Ensure that the eMMC is in the Transfer State before proceeding */ if ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER) { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD7_SELECT_DESELECT_CARD | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1B; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Wait for eMMC to enter Transfer State */ do { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER); } /* Set the block length in bytes and the number of blocks to write to the SD card */ MMC1_REG(SD_BLK) = 0x200 | (blkcnt << 16); /* Send CMD25, that is write the number of blocks specified in 'blkcount' from 'buf' to the * location that is 512 byte aligned in the SD card specified by the block number 'blknum' */ arg = blknum; cmd = SD_CMD_CMD25_WRITE_MULTIPLE_BLOCK | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1 | SD_CMD_MSBS_MULTIPLE | SD_CMD_DDIR_WRITE | SD_CMD_ACEN_CMD12_ENABLE | SD_CMD_BCE_ENABLE; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Write the data */ do { /* Wait until data is ready to be written */ while (!(MMC1_REG(SD_STAT) & (SD_STAT_BWR | SD_STAT_TC))); if (MMC1_REG(SD_STAT) & SD_STAT_TC) break; /* Clear the BWR status bit in SD_STAT */ MMC1_REG(SD_STAT) |= SD_STAT_BWR; for (byteindex = 0; byteindex < (0x200 / 4); byteindex++) MMC1_REG(SD_DATA) = *wordptr++; } while (!(MMC1_REG(SD_STAT) & SD_STAT_TC)); /* Put the eMMC into Stand-by State */ arg = 0x00000000; cmd = SD_CMD_CMD7_SELECT_DESELECT_CARD | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_NO_RESPONSE; if (mmccmd(cmd, arg, resp) == -1) return(-1); /* Wait for eMMC to enter Stand-by State */ do { arg = (mmcrca << 16) & 0xFFFF0000; cmd = SD_CMD_CMD13_SEND_STATUS | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT | SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R1; if (mmccmd(cmd, arg, resp) == -1) return(-1); } while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY); return(0); } int mmcInstalled(int interface) { return(1); }