From d8325a15269a0de8948bf729b22ad125c99cccbd Mon Sep 17 00:00:00 2001 From: Jarielle Catbagan Date: Tue, 4 Aug 2015 11:03:25 -0700 Subject: BBB: Add am335x_mmc.c and am335x_mmc.h that implement "mmc" command with initialization functionality --- ports/beagleboneblack/am335x_mmc.c | 347 +++++++++++++++++++++++++++++++++++++ ports/beagleboneblack/am335x_mmc.h | 13 ++ 2 files changed, 360 insertions(+) create mode 100644 ports/beagleboneblack/am335x_mmc.c create mode 100644 ports/beagleboneblack/am335x_mmc.h diff --git a/ports/beagleboneblack/am335x_mmc.c b/ports/beagleboneblack/am335x_mmc.c new file mode 100644 index 0000000..f045fec --- /dev/null +++ b/ports/beagleboneblack/am335x_mmc.c @@ -0,0 +1,347 @@ +/* + * 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]; + + /* Enable MMC1 clocks */ + CM_PER_REG(CM_PER_MMC1_CLKCTRL) |= CM_PER_MMC1_CLKCTRL_MODULEMODE_ENABLE; + while (CM_PER_REG(CM_PER_MMC1_CLKCTRL) & CM_PER_MMC0_CLKCTRL_IDLEST); + + /* 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) +{ + return(-1); +} + +int +mmcWrite(int interface, char *buf, int blknum, int blkcnt) +{ + return(-1); +} + +int +mmcInstalled(int interface) +{ + return(1); +} diff --git a/ports/beagleboneblack/am335x_mmc.h b/ports/beagleboneblack/am335x_mmc.h new file mode 100644 index 0000000..aae1b16 --- /dev/null +++ b/ports/beagleboneblack/am335x_mmc.h @@ -0,0 +1,13 @@ +/* + * 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 + */ + +int mmc(int argc, char *argv[]); +int mmcInit(int interface, int verbose); +int mmcRead(int interface, char *buf, int blknum, int blkcnt); +int mmcWrite(int interface, char *buf, int blknum, int blkcnt); +int mmcInstalled(int interface); -- cgit v1.2.3