summaryrefslogblamecommitdiffstats
path: root/ports/beagleboneblack/am335x_sd.c
blob: 7eb0180014bed0452b93bfb3932500f43985ab44 (plain) (tree)
















































                                                                                              






























































































































































































                                                                                                     






































































                                                                                                      




                                                           










































































                                                                                                      




                          









                                                                        












                                                     
/*
 * Copyright (c) 2015 Jarielle Catbagan <jcatbagan93@gmail.com>
 *
 * 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 "stddefs.h"
#include "am335x.h"
#include "sd.h"

uint16_t sdrca;

int
sdcmd(uint32_t cmd, uint32_t arg, uint32_t resp[4])
{
	/* Clear the SD_STAT register for proper update of status bits after CMD invocation */
	MMCHS0_REG(SD_STAT) = 0xFFFFFFFF;

	MMCHS0_REG(SD_ARG) = arg;
	MMCHS0_REG(SD_CMD) = cmd;

	/* CMDx complete? */
	while (!(MMCHS0_REG(SD_STAT) & (SD_STAT_CC | SD_STAT_ERRI)));

	resp[0] = MMCHS0_REG(SD_RSP10);
	resp[1] = MMCHS0_REG(SD_RSP32);
	resp[2] = MMCHS0_REG(SD_RSP54);
	resp[3] = MMCHS0_REG(SD_RSP76);

	/* CMDx error? */
	if (MMCHS0_REG(SD_STAT) & SD_STAT_ERRI)
		return(-1);
	else
		return(0);
}

int
sdCardCmd(int interface, int cmd, unsigned long arg, unsigned char *resp)
{
	return(-1);
}

int
sdInit(int interface, int verbosity)
{
	uint32_t cmd, arg, resp[4];

	/* Reset the MMC/SD controller */
	MMCHS0_REG(SD_SYSCONFIG) = SD_SYSCONFIG_SOFTRESET;
	while (!(MMCHS0_REG(SD_SYSSTATUS) & SD_SYSSTATUS_RESETDONE));

	/* Reset the command and data lines */
	MMCHS0_REG(SD_SYSCTL) |= SD_SYSCTL_SRA;
	while (MMCHS0_REG(SD_SYSCTL) & SD_SYSCTL_SRA);

	/* Configure the MMC/SD controller capabilities to enable 3.0 V operating voltage */
	MMCHS0_REG(SD_CAPA) = SD_CAPA_VS30;

	/* Configure SD_IE register to update certain status bits in SD_STAT */
	MMCHS0_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;

	/* Disable open-drain mode (only used for MMC cards) and 8-bit data width  */
	MMCHS0_REG(SD_CON) &= ~(SD_CON_OD | SD_CON_DW8);

	/* Configure the operating voltage to 3.0 V */
	MMCHS0_REG(SD_HCTL) &= ~(SD_HCTL_SDVS);
	MMCHS0_REG(SD_HCTL) |= SD_HCTL_SDVS_VS30;

	/* Set the data width to 4-bits */
	MMCHS0_REG(SD_HCTL) |= SD_HCTL_DTW;

	/* Turn on the bus */
	MMCHS0_REG(SD_HCTL) |= SD_HCTL_SDBP;
	while (!(MMCHS0_REG(SD_HCTL) & SD_HCTL_SDBP));

	/* Enable the internal clock */
	MMCHS0_REG(SD_SYSCTL) |= SD_SYSCTL_ICE;

	/* Configure Clock Frequency Select to 100 KHz */
	MMCHS0_REG(SD_SYSCTL) = (MMCHS0_REG(SD_SYSCTL) & ~(SD_SYSCTL_CLKD)) | (960 << 6);

	/* Wait for clock to stabilize */
	while (!(MMCHS0_REG(SD_SYSCTL) & SD_SYSCTL_ICS));

	/* Configure SD_SYSCONFIG */
	MMCHS0_REG(SD_SYSCONFIG) &= ~(SD_SYSCONFIG_CLOCKACTIVITY | SD_SYSCONFIG_SIDLEMODE);
	MMCHS0_REG(SD_SYSCONFIG) |= SD_SYSCONFIG_SIDLEMODE_WKUP | SD_SYSCONFIG_ENAWAKEUP_ENABLE |
		SD_SYSCONFIG_AUTOIDLE_AUTOGATE;

	/* Enable the clock to the SD card */
	MMCHS0_REG(SD_SYSCTL) |= SD_SYSCTL_CEN_ENABLE;

	/* Perform the Initialization Stream as specified in the AM335x TRM, Section 18.3.3.2
	   "Card Detection, Identicication, and Selection" */
	MMCHS0_REG(SD_CON) |= SD_CON_INIT;
	/* Clear the SD_STAT register */
	MMCHS0_REG(SD_STAT) = 0xFFFFFFFF;
	MMCHS0_REG(SD_ARG) = 0x00000000;
	MMCHS0_REG(SD_CMD) = 0x00000000;
	while (!(MMCHS0_REG(SD_STAT) & SD_STAT_CC));
	/* Clear CC flag in SD_STAT */
	MMCHS0_REG(SD_STAT) |= SD_STAT_CC;
	MMCHS0_REG(SD_CON) &= ~SD_CON_INIT;

	/* Change the clock frequency to 6 MHz and set the DTO to the maximum value setting */
	MMCHS0_REG(SD_SYSCTL) &= ~SD_SYSCTL_DTO;
	MMCHS0_REG(SD_SYSCTL) |= SD_SYSCTL_DTO_TCF_2_27;
	MMCHS0_REG(SD_SYSCTL) = (MMCHS0_REG(SD_SYSCTL) & ~SD_SYSCTL_CLKD) | (16 << 6);

	/* Wait for clock to stabilize */
	while ((MMCHS0_REG(SD_SYSCTL) & SD_SYSCTL_ICS) != SD_SYSCTL_ICS);

	/* Send CMD0/GO_IDLE_STATE to reset the SD card connected to MMC0 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Send CMD8/SEND_IF_COND to verify that the SD card satisfies MMC/SD controller
	   requirements */
	arg = 0x00000188;
	cmd = SD_CMD_CMD8_SEND_IF_COND | SD_CMD_CMD_TYPE_NORMAL | SD_CMD_DP_NO_DATA_PRESENT |
		SD_CMD_CICE_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_R7;
	if (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Determine what type of SD card is connected, i.e. standard capacity, high capacity, etc. 
	 * We perform a CMD55/ACMD41 loop until the "Card power up status bit" is set in the OCR
	 * register from the SD card to determine when we have a valid response */
	do {
		arg = 0x00000000;
		cmd = SD_CMD_CMD55_APP_CMD | 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);

		arg = 0x40060000;
		cmd = SD_CMD_ACMD41_SD_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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);
	} while (!(resp[0] & SD_RSP10_R3_CARD_POWER_UP_STATUS));

	/* Check SD_RSP10 to determine whether the card connected is high capacity or not */
	if (resp[0] & SD_RSP10_R3_CARD_CAPACITY_STATUS)
		sdInfoTbl[interface].highcapacity = 1;
	else
		sdInfoTbl[interface].highcapacity = 0;

	/* Send CMD2 to get SD's CID and to put the card into Identification State */
	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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Send CMD3, i.e. request new relative address (RCA) and to put the card into
	   Stand-by State */
	arg = 0x00000000;
	cmd = SD_CMD_CMD3_SEND_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_R6;
	if (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Save the RCA published from the SD card, this will be used in future CMDx commands */
	sdrca = (MMCHS0_REG(SD_RSP10) >> 16) & 0xFFFF;

	/* Wait for the SD card to enter Stand-by State */
	do {
		arg = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);
	} while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY);

	/* Put the card with the RCA obtained previously into Transfer State via CMD7 */
	arg = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Wait for the SD card to enter Transfer State */
	do {
		arg = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);
	} while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER);

	/* Set the bus-width to 4-bits */

	/* Send CMD55 to get ready to configure the bus-width via ACMD6 */
	arg = (sdrca << 16) & 0xFFFF0000;
	cmd = SD_CMD_CMD55_APP_CMD | 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Send ACMD6, SET_BUS_WIDTH to set the bus-width to 4-bits */
	arg = 0x00000002;
	cmd = SD_CMD_ACMD6_SET_BUS_WIDTH | 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Put the SD card 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Wait for the SD card to enter Stand-by State */
	do {
		arg = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);
	} while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY);

	/* This point is reached when SD controller initialization and SD card
	   communication is successful */
	return(0);
}

int
sdRead(int interface, char *buf, int blknum, int blkcount)
{
	uint32_t cmd, arg, resp[4];
	uint32_t *wordptr = (uint32_t *) buf;
	int byteindex;

	/* Get the SD card's status via CMD13 */
	arg = (sdrca << 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 (sdcmd(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 = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);

		/* Wait for the SD card to enter Transfer State */
		do {
			arg = (sdrca << 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 (sdcmd(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 */
	MMCHS0_REG(SD_BLK) = SD_BLKSIZE | (blkcount << 16);
	/* Send CMD18, i.e. read multiple blocks */
	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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Check the data buffer to see if there is data to be read */
	do {
		while (!(MMCHS0_REG(SD_STAT) & SD_STAT_BRR));

		/* Clear the BRR status bit in SD_STAT */
		MMCHS0_REG(SD_STAT) |= SD_STAT_BRR;

		for (byteindex = 0; byteindex < (SD_BLKSIZE / 4); byteindex++) {
			*wordptr = (MMCHS0_REG(SD_DATA));
			wordptr++;
		}
	} while (!(MMCHS0_REG(SD_STAT) & SD_STAT_TC));

	/* Put the SD card 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_ENABLE | SD_CMD_CCCE_ENABLE | SD_CMD_RSP_TYPE_NO_RESPONSE;
	if (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Wait for the SD card to enter Stand-by State */
	do {
		arg = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);
	} while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY);

	return(0);
}

int
sdWrite(int interface, char *buf, int blknum, int blkcount)
{
	uint32_t cmd, arg, resp[4];
	uint32_t *wordptr = (uint32_t *) buf;
	int byteindex;

	/* Get the SD card's status by sending CMD13 */
	arg = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Ensure that the card is in the Transfer State before proceeding */
	if ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_TRANSFER) {
		arg = (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);

		/* Wait for SD card to enter Transfer State */
		do {
			arg = (sdrca << 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 (sdcmd(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 */
	MMCHS0_REG(SD_BLK) = SD_BLKSIZE | ( blkcount << 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Write the data */
	do {
		/* Wait until data is ready to be written */
		while (!(MMCHS0_REG(SD_STAT) & (SD_STAT_BWR | SD_STAT_TC)));

		if (MMCHS0_REG(SD_STAT) & SD_STAT_TC)
			break;

		/* Clear the BWR status bit in SD_STAT */
		MMCHS0_REG(SD_STAT) |= SD_STAT_BWR;

		for (byteindex = 0; byteindex < (SD_BLKSIZE / 4); byteindex++)
			MMCHS0_REG(SD_DATA) = *wordptr++;
	} while (!(MMCHS0_REG(SD_STAT) & SD_STAT_TC));

	/* Put the SD card 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 (sdcmd(cmd, arg, resp) == -1)
		return(-1);

	/* Wait for SD card to enter Stand-by State */
	do {
		arg= (sdrca << 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 (sdcmd(cmd, arg, resp) == -1)
			return(-1);
	} while ((resp[0] & SD_RSP10_R1_CURRENT_STATE) != SD_RSP10_R1_CURRENT_STATE_STANDBY);

	return(0);
}

int
sdInstalled(int interface)
{
	if ((MMCHS0_REG(SD_CON) & SD_CON_CDP) == SD_CON_CDP_ACTIVE_HIGH)
		if (MMCHS0_REG(SD_PSTATE) & SD_PSTATE_CINS)
			return(1);
		else
			return(0);
	else
		if (MMCHS0_REG(SD_PSTATE) & SD_PSTATE_CINS)
			return(0);
		else
			return(1);
}

int
sdPowerup(int tot)
{
	return(-1);
}

int
sdReadCxD(int interface, unsigned char *buf, int cmd)
{
	return(-1);
}