SAME70 SD access via HSMCI with clock frequency greater than 400 kHz

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've managed to get SD read/write access working on the SAME70 Xplained board using the HSMCI peripheral, but only if I leave the HSMCI clock divider set so that the clock continues to operate at the initialization/identification frequency (400 kHz). Per section 4.3 in the Physical Layer Simplified Specification version 6, I should be able to switch the HSMCI clock to 25 MHz after the identification mode completes. However, when I do, commands at the higher frequency fail with an error indicated in the status register.

 

Has anyone successfully used the HSMCI at (or near) 25 MHz?

 

I've included my code for reference:

 

#define SD_CLKDIV_400KHZ 184
#define SD_CLKDIV_25MHZ 2

#define SD_ACMD41_HCS   (1lu << 30) /* (SD) Host Capacity Support */

#define OCR_REG_BSIZE          (32 / 8)    /**< 32 bits, 4 bytes */
#define OCR_VDD_170_195        (1lu << 7)
#define OCR_VDD_20_21          (1lu << 8)
#define OCR_VDD_21_22          (1lu << 9)
#define OCR_VDD_22_23          (1lu << 10)
#define OCR_VDD_23_24          (1lu << 11)
#define OCR_VDD_24_25          (1lu << 12)
#define OCR_VDD_25_26          (1lu << 13)
#define OCR_VDD_26_27          (1lu << 14)
#define OCR_VDD_27_28          (1lu << 15)
#define OCR_VDD_28_29          (1lu << 16)
#define OCR_VDD_29_30          (1lu << 17)
#define OCR_VDD_30_31          (1lu << 18)
#define OCR_VDD_31_32          (1lu << 19)
#define OCR_VDD_32_33          (1lu << 20)
#define OCR_VDD_33_34          (1lu << 21)
#define OCR_VDD_34_35          (1lu << 22)
#define OCR_VDD_35_36          (1lu << 23)
#define OCR_SDIO_S18R          (1lu << 24) /**< Switching to 1.8V Accepted */
#define OCR_SDIO_MP            (1lu << 27) /**< Memory Present */
#define OCR_SDIO_NF            (7lu << 28) /**< Number of I/O Functions */
#define OCR_ACCESS_MODE_MASK   (3lu << 29) /**< (MMC) Access mode mask */
#define OCR_ACCESS_MODE_BYTE   (0lu << 29) /**< (MMC) Byte access mode */
#define OCR_ACCESS_MODE_SECTOR (2lu << 29) /**< (MMC) Sector access mode */
#define OCR_CCS                (1lu << 30) /**< (SD) Card Capacity Status */
#define OCR_POWER_UP_BUSY      (1lu << 31) /**< Card power up status bit */

#define SD_MMC_VOLTAGE_SUPPORT (OCR_VDD_27_28 | OCR_VDD_28_29 | OCR_VDD_29_30 | OCR_VDD_30_31 | OCR_VDD_31_32 | OCR_VDD_32_33)

#define CARD_STATUS_APP_CMD           (1lu << 5)
#define CARD_STATUS_SWITCH_ERROR      (1lu << 7)
#define CARD_STATUS_READY_FOR_DATA    (1lu << 8)
#define CARD_STATUS_STATE_IDLE        (0lu << 9)
#define CARD_STATUS_STATE_READY       (1lu << 9)
#define CARD_STATUS_STATE_IDENT       (2lu << 9)
#define CARD_STATUS_STATE_STBY        (3lu << 9)
#define CARD_STATUS_STATE_TRAN        (4lu << 9)
#define CARD_STATUS_STATE_DATA        (5lu << 9)
#define CARD_STATUS_STATE_RCV         (6lu << 9)
#define CARD_STATUS_STATE_PRG         (7lu << 9)
#define CARD_STATUS_STATE_DIS         (8lu << 9)
#define CARD_STATUS_STATE             (0xFlu << 9)
#define CARD_STATUS_ERASE_RESET       (1lu << 13)
#define CARD_STATUS_WP_ERASE_SKIP     (1lu << 15)
#define CARD_STATUS_CIDCSD_OVERWRITE  (1lu << 16)
#define CARD_STATUS_OVERRUN           (1lu << 17)
#define CARD_STATUS_UNERRUN           (1lu << 18)
#define CARD_STATUS_ERROR             (1lu << 19)
#define CARD_STATUS_CC_ERROR          (1lu << 20)
#define CARD_STATUS_CARD_ECC_FAILED   (1lu << 21)
#define CARD_STATUS_ILLEGAL_COMMAND   (1lu << 22)
#define CARD_STATUS_COM_CRC_ERROR     (1lu << 23)
#define CARD_STATUS_UNLOCK_FAILED     (1lu << 24)
#define CARD_STATUS_CARD_IS_LOCKED    (1lu << 25)
#define CARD_STATUS_WP_VIOLATION      (1lu << 26)
#define CARD_STATUS_ERASE_PARAM       (1lu << 27)
#define CARD_STATUS_ERASE_SEQ_ERROR   (1lu << 28)
#define CARD_STATUS_BLOCK_LEN_ERROR   (1lu << 29)
#define CARD_STATUS_ADDRESS_MISALIGN  (1lu << 30)
#define CARD_STATUS_ADDR_OUT_OF_RANGE (1lu << 31)

#define CARD_STATUS_ERR_RD_WR \
		( CARD_STATUS_ADDR_OUT_OF_RANGE \
		| CARD_STATUS_ADDRESS_MISALIGN \
		| CARD_STATUS_BLOCK_LEN_ERROR \
		| CARD_STATUS_WP_VIOLATION \
		| CARD_STATUS_ILLEGAL_COMMAND \
		| CARD_STATUS_CC_ERROR \
		| CARD_STATUS_ERROR)

#define SD_CMD_BASE (HSMCI_CMDR_MAXLAT_64 | HSMCI_CMDR_OPDCMD_OPENDRAIN)
#define SD_CMD_INIT (SD_CMD_BASE | HSMCI_CMDR_SPCMD_INIT)
#define SD_CMD0 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(0))
#define SD_CMD2 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(2) | HSMCI_CMDR_RSPTYP_136_BIT)
#define SD_CMD3 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(3) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_CMD4 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(4))
#define SD_CMD6 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(6) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_CMD7 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(7) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_CMD8 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(8) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_CMD9 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(9) | HSMCI_CMDR_RSPTYP_136_BIT)
#define SD_CMD13 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(13) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_CMD16 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(16) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_CMD17 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(17) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_READ | HSMCI_CMDR_TRTYP_SINGLE)
#define SD_CMD24 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(24) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_WRITE | HSMCI_CMDR_TRTYP_SINGLE)
#define SD_CMD55 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(55) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_CMD59 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(59))
#define SD_ACMD41 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(41) | HSMCI_CMDR_RSPTYP_48_BIT)
#define SD_ACMD6 (SD_CMD_BASE | HSMCI_CMDR_CMDNB(6) | HSMCI_CMDR_RSPTYP_48_BIT)

#define HSMCI_SR_ERROR (HSMCI_SR_RINDE | HSMCI_SR_RDIRE | HSMCI_SR_RCRCE | HSMCI_SR_RENDE | HSMCI_SR_RTOE | HSMCI_SR_DCRCE | HSMCI_SR_DTOE | HSMCI_SR_CSTOE | HSMCI_SR_BLKOVRE | HSMCI_SR_ACKRCVE | HSMCI_SR_OVRE | HSMCI_SR_UNRE)

SdHsmci _sd_hsmci;
HsmciParams _hsmci_params = {
	XDMA_HSMCI_TX_RX, 0
};

extern "C" {

// DMA interrupt

void SD_Handler(uint32_t channel) {
	HsmciParams *p = &_hsmci_params;
	if (XDMAC->XDMAC_CHID[p->dma_channel].XDMAC_CIS & XDMAC_CIS_BIS) {
		_xdma.Disable(p->dma_channel);
		p->owner->status = WAKE;
	}
}

// command interrupt

void HSMCI_Handler(void) {
	uint32_t status = HSMCI->HSMCI_SR;
	if (status & HSMCI_SR_CMDRDY & HSMCI->HSMCI_IMR) {
		HSMCI->HSMCI_IDR = HSMCI_IDR_CMDRDY;
		_hsmci_params.owner->status = WAKE;
	}
}

}

void SdHsmci::Init(uint32_t retries) {
	// configure the I/O lines
	// HSMCI uses the following pins:
	//   PA25 Peripheral D => MCCK
	//   PA26 Peripheral C => MCDA2
	//   PA27 Peripheral C => MCDA3
	//   PA28 Peripheral C => MCCDA
	//   PA30 Peripheral C => MCDA0
	//   PA31 Peripheral C => MCDA1
	PIO_DISABLE(PIOA, 0, 0xde000000);
	PIO_NO_PULL_UP(PIOA, 0, 0xde000000);
	PIO_CHOOSE_C(PIOA, 0, 0xdc000000);
	PIO_CHOOSE_D(PIOA, 0, 0x02000000);
	// enable the peripheral clock
	PMC->PMC_PCER0 = 1 << ID_HSMCI;

	// assign DMA channels
	HsmciParams *p = params_ = &_hsmci_params;
	if ((p->dma_channel = _xdma.GetAvailableChannel()) < 0) return;
	_xdma.SetHandler(p->dma_channel, SD_Handler);
	// before we initialize XDMAC, disable the HSMCI
	HSMCI->HSMCI_CR = HSMCI_CR_MCIDIS; // disable HSMCI
	HSMCI->HSMCI_IDR = ~0; // disable all interrupts
	_xdma.Disable(p->dma_channel);
	// setup HSMCI
	HSMCI->HSMCI_DTOR = HSMCI_DTOR_DTOMUL_1048576 | HSMCI_DTOR_DTOCYC(2); // set the data timeout register to 2 mega cycles
	HSMCI->HSMCI_CSTOR = HSMCI_CSTOR_CSTOMUL_1048576 | HSMCI_CSTOR_CSTOCYC(2); // set completion signal timeout to 2 mega cycles
	HSMCI->HSMCI_CFG =
		HSMCI_CFG_FIFOMODE |
		HSMCI_CFG_FERRCTRL;
	HSMCI->HSMCI_SDCR = HSMCI_SDCR_SDCBUS_4; // use 4 data lines
	HSMCI->HSMCI_MR =
		HSMCI_MR_CLKDIV(SD_CLKDIV_400KHZ) | // 400 kHz, required when initializing a SD card
		HSMCI_MR_PWSDIV(0x7) | // maximum power saving
		HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF; // ensure data integrity
	HSMCI->HSMCI_CR =
		HSMCI_CR_MCIEN | // enable HSMCI
		HSMCI_CR_PWSEN; // enable power saving
	// enable interrupts on HSMCI (we'll use an interrupt on CMDRDY to be notified when a command completes)
	NVIC_DisableIRQ(HSMCI_IRQn);
	NVIC_ClearPendingIRQ(HSMCI_IRQn);
	NVIC_SetPriority(HSMCI_IRQn, 0);
	NVIC_EnableIRQ(HSMCI_IRQn);
	// initialize the member variables
	retries_ = retries;
	error_ = 0;
}

// returns: 0 if no error
uint32_t SdHsmci::command(uint32_t cmd, uint32_t arg) {
	params_->owner = Active;
	HSMCI->HSMCI_SR;
	HSMCI->HSMCI_IER = HSMCI_IER_CMDRDY;
	HSMCI->HSMCI_ARGR = arg;
	HSMCI->HSMCI_CMDR = cmd;
	task::Stop(); // stop the calling task and wait for the command to complete
	return HSMCI->HSMCI_SR & HSMCI_SR_ERROR;
}

uint8_t SdHsmci::err(uint32_t error) {
	// we only have 8-bits to report errors
	return (error & HSMCI_SR_UNRE) >> 24 |
			   (error & HSMCI_SR_OVRE) >> 24 |
				 (error & HSMCI_SR_BLKOVRE) >> 19 |
				 (error & HSMCI_SR_CSTOE) >> 19 |
				 (error & HSMCI_SR_DTOE) >> 19 |
				 (error & HSMCI_SR_DCRCE) >> 19 |
				 (error & HSMCI_SR_RTOE) >> 19 |
				 (error & HSMCI_SR_RCRCE) >> 18;
}

uint32_t SdHsmci::Reset() {
	uint32_t r, resp;
	int32_t retries;
	// send clock (at least 74 cycles)
	if ((r = command(SD_CMD_INIT, 0)) != 0) return error_ = 0x000 + err(r);
	// go idle
	if ((r = command(SD_CMD0, 0)) != 0) return error_ = 0x100 + err(r);
	if ((r = command(SD_CMD59, 1)) != 0) return error_ = 0x200 + err(r);
	// send interface condition (check if the card supports 2.7V-3.6V operation)
	retries = retries_; // for some reason it takes a couple retries to get a valid response
	do {
		if ((r = command(SD_CMD8, 0x1aa)) != 0) return error_ = 0x300 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
	} while (--retries > 0 && (resp & 0xfff) != 0x1aa);
	// start card initialization
	retries = 25000;
	do {
		if ((r = command(SD_CMD55, 0)) != 0) return error_ = 0x400 + err(r);
		if ((r = command(SD_ACMD41, SD_ACMD41_HCS | SD_MMC_VOLTAGE_SUPPORT) & ~HSMCI_SR_RCRCE) != 0) return error_ = 0x500 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
		if (resp & OCR_POWER_UP_BUSY) {
			hc_ = (resp & OCR_CCS) != 0;
			break;
		}
	} while (retries-- > 0);
	if (retries < 0) return error_ = 0x600 + err(1); // timeout on busy
	// initialization complete
	// ask for CID number (put card in identify mode)
	if ((r = command(SD_CMD2, 0)) != 0) return error_ = 0x700 + err(r);
	// ask the card to send relative address
	if ((r = command(SD_CMD3, 0)) != 0) return error_ = 0x800 + err(r);
	resp = HSMCI->HSMCI_RSPR[0];
	rca_ = (resp >> 16) & 0xffff;
	if ((r = GetInfo()) != 0) return error_ = 0x900 + err(r);
	// select the card (we'll only select it once since we only have one card and we don't intend for the card to be replaced once initialized)
	if ((r = command(SD_CMD7, rca_ << 16)) != 0) return error_ = 0xa00 + err(r);
	// set bus width to 4-bits
	if ((r = command(SD_CMD55, rca_ << 16)) != 0) return error_ = 0xb00 + err(r);
	if ((r = command(SD_ACMD6, 2)) != 0) return error_ = 0xc00 + err(r);
  // card identification is complete, switch to higher clock frequency
  /*HSMCI->HSMCI_MR =
   HSMCI_MR_CLKDIV(SD_CLKDIV_25MHZ) | // we can clock data transfers at up to 25 MHz
   HSMCI_MR_PWSDIV(0x7) | // maximum power saving
   HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF;*/ // ensure data integrity
  return 0;
}

uint32_t SdHsmci::GetInfo() {
	uint32_t r;
	if ((r = command(SD_CMD9, rca_ << 16)) != 0) return error_ = 0xd00 + err(r);
	csd_[0] = HSMCI->HSMCI_RSPR[0]; // bits 96-127
	csd_[1] = HSMCI->HSMCI_RSPR[1]; // bits 64-95
	csd_[2] = HSMCI->HSMCI_RSPR[2]; // bits 32-63
	csd_[3] = HSMCI->HSMCI_RSPR[3]; // bits 8-31
	return 0;
}

uint32_t SdHsmci::ReadBlock(char *buffer, uint32_t block) {
	uint32_t r, resp;
	int32_t retries;
	// wait for card status to indicate ready for data
	retries = 200000;
	do {
		if ((r = command(SD_CMD13, rca_ << 16)) != 0) return error_ = 0xe00 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
		if (resp & CARD_STATUS_READY_FOR_DATA) break;
		else task::Pause();
	} while (retries-- > 0);
	if (retries < 0) return error_ = 0xf00; // timeout occured
	// set block length
	if ((r = command(SD_CMD16, 512)) != 0) return error_ = 0x1000 + err(r);
	HSMCI->HSMCI_DMA =
		HSMCI_DMA_DMAEN |
		HSMCI_DMA_CHKSIZE_16;
	HSMCI->HSMCI_BLKR = HSMCI_BLKR_BLKLEN(512) | HSMCI_BLKR_BCNT(1);
	// request a single block
	if ((r = command(SD_CMD17, block << (hc_ ? 0 : 9))) != 0) return error_ = 0x1100 + err(r);
	resp = HSMCI->HSMCI_RSPR[0];
	if (resp & CARD_STATUS_ERR_RD_WR) return error_ = 0x1200;
	// configure DMA to receive data
	_xdma.Configure(params_->dma_channel,
		XDMAC_CC_PERID(params_->dma_peripheral) |
		XDMAC_CC_SAM_FIXED_AM |
		XDMAC_CC_DAM_INCREMENTED_AM |
		XDMAC_CC_DWIDTH_WORD |
		XDMAC_CC_CSIZE_CHK_16 |
		XDMAC_CC_MEMSET_NORMAL_MODE |
		XDMAC_CC_SIF_AHB_IF1 |
		XDMAC_CC_DIF_AHB_IF0 |
		XDMAC_CC_DSYNC_PER2MEM |
		XDMAC_CC_MBSIZE_SINGLE |
		XDMAC_CC_TYPE_PER_TRAN,
		0,
		0
	);
	_xdma.Enable(params_->dma_channel, (uint32_t)&HSMCI->HSMCI_FIFO[0], (uint32_t)buffer, 128);
	// wait for the transfer to complete
	params_->owner = Active;
	task::Stop();
	return 0;
}

uint32_t SdHsmci::WriteBlock(char *buffer, uint32_t block) {
	uint32_t r, resp;
	int32_t retries;
	// wait for the card status to indicate ready for data
	retries = 200000;
	do {
		if ((r = command(SD_CMD13, rca_ << 16)) != 0) return error_ = 0x1300 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
		if (resp & CARD_STATUS_READY_FOR_DATA) break;
		else task::Pause();
	} while (retries-- > 0);
	if (retries < 0) return error_ = 0x1400; // timeout occured
	if ((r = command(SD_CMD16, 512)) != 0) return error_ = 0x1500 + err(r);
	HSMCI->HSMCI_DMA =
		HSMCI_DMA_DMAEN |
		HSMCI_DMA_CHKSIZE_16;
	HSMCI->HSMCI_BLKR = HSMCI_BLKR_BLKLEN(512) | HSMCI_BLKR_BCNT(1);
	// write a single block
	if ((r = command(SD_CMD24, block << (hc_ ? 0 : 9))) != 0) return error_ = 0x1600 + err(r);
	resp = HSMCI->HSMCI_RSPR[0];
	if (resp & CARD_STATUS_ERR_RD_WR) return error_ = 0x1700 + err(r);
	HSMCI->HSMCI_MR =
		HSMCI_MR_CLKDIV(SD_CLKDIV_25MHZ) | // we can clock data transfers at up to 25 MHz
		HSMCI_MR_PWSDIV(0x7) | // maximum power saving
		HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF; // ensure data integrity
	// configure DMA to receive data
	_xdma.Configure(params_->dma_channel,
		XDMAC_CC_PERID(params_->dma_peripheral) |
		XDMAC_CC_DAM_FIXED_AM |
		XDMAC_CC_SAM_INCREMENTED_AM |
		XDMAC_CC_DWIDTH_WORD |
		XDMAC_CC_CSIZE_CHK_16 |
		XDMAC_CC_MEMSET_NORMAL_MODE |
		XDMAC_CC_SIF_AHB_IF0 |
		XDMAC_CC_DIF_AHB_IF1 |
		XDMAC_CC_DSYNC_MEM2PER |
		XDMAC_CC_MBSIZE_SINGLE |
		XDMAC_CC_TYPE_PER_TRAN,
		0,
		0
	);
	_xdma.Enable(params_->dma_channel, (uint32_t)buffer, (uint32_t)&HSMCI->HSMCI_FIFO[0], 128);
	// wait for the transfer to complete
	params_->owner = Active;
	task::Stop();
	return 0;
}

 

This topic has a solution.
Last Edited: Thu. Aug 30, 2018 - 11:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

By experimentation, it looks like the HSMCI stops sending commands on CDA correctly around 1.16 MHz.

 

Here's what the first command in ReadBlock (CMD13) looks like at ~300 kHz:

 

 

Here's what the same command looks like at ~1.185 MHz:

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What type and speed of SD card?  From https://www.sdcard.org/developer...

 

 

 

Last Edited: Wed. Aug 29, 2018 - 06:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't check the HSMCI specs closely.  But remember there are four data lines and the bus speeds are specified in megaBYTES per second, not megaBITS per second.  Clock speeds would be much slower (ie. 50MB/s = 50,000,000 / 8 / 4 =  ~1.5MHz clock)

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was able to get it to work through a closer analysis of Atmel's sample SD project available in Atmel Studio. Most of the changes were either command bits or configuration register values at different points of initialization or transfer (see the updated code listed below). I can't see anything in the SAM E70 datasheet that would have prompted me to make the changes I did, so most were made because it's how Atmel did it in their example.

 

I've been testing with both a standard microSD and a HC microSD. The 25 MHz clock frequency is considered the default speed (see section 2 of the SD physical layer simplified specification) and should be supported by all SD cards as I understand it.

 

#include "sd_hsmci.h"
#include "example.h" // TODO: for debugging

#define SD_ACMD41_HCS   (1lu << 30) /* (SD) Host Capacity Support */

#define OCR_REG_BSIZE          (32 / 8)    /**< 32 bits, 4 bytes */
#define OCR_VDD_170_195        (1lu << 7)
#define OCR_VDD_20_21          (1lu << 8)
#define OCR_VDD_21_22          (1lu << 9)
#define OCR_VDD_22_23          (1lu << 10)
#define OCR_VDD_23_24          (1lu << 11)
#define OCR_VDD_24_25          (1lu << 12)
#define OCR_VDD_25_26          (1lu << 13)
#define OCR_VDD_26_27          (1lu << 14)
#define OCR_VDD_27_28          (1lu << 15)
#define OCR_VDD_28_29          (1lu << 16)
#define OCR_VDD_29_30          (1lu << 17)
#define OCR_VDD_30_31          (1lu << 18)
#define OCR_VDD_31_32          (1lu << 19)
#define OCR_VDD_32_33          (1lu << 20)
#define OCR_VDD_33_34          (1lu << 21)
#define OCR_VDD_34_35          (1lu << 22)
#define OCR_VDD_35_36          (1lu << 23)
#define OCR_SDIO_S18R          (1lu << 24) /**< Switching to 1.8V Accepted */
#define OCR_SDIO_MP            (1lu << 27) /**< Memory Present */
#define OCR_SDIO_NF            (7lu << 28) /**< Number of I/O Functions */
#define OCR_ACCESS_MODE_MASK   (3lu << 29) /**< (MMC) Access mode mask */
#define OCR_ACCESS_MODE_BYTE   (0lu << 29) /**< (MMC) Byte access mode */
#define OCR_ACCESS_MODE_SECTOR (2lu << 29) /**< (MMC) Sector access mode */
#define OCR_CCS                (1lu << 30) /**< (SD) Card Capacity Status */
#define OCR_POWER_UP_BUSY      (1lu << 31) /**< Card power up status bit */

#define SD_MMC_VOLTAGE_SUPPORT (OCR_VDD_27_28 | OCR_VDD_28_29 | OCR_VDD_29_30 | OCR_VDD_30_31 | OCR_VDD_31_32 | OCR_VDD_32_33)

#define CARD_STATUS_APP_CMD           (1lu << 5)
#define CARD_STATUS_SWITCH_ERROR      (1lu << 7)
#define CARD_STATUS_READY_FOR_DATA    (1lu << 8)
#define CARD_STATUS_STATE_IDLE        (0lu << 9)
#define CARD_STATUS_STATE_READY       (1lu << 9)
#define CARD_STATUS_STATE_IDENT       (2lu << 9)
#define CARD_STATUS_STATE_STBY        (3lu << 9)
#define CARD_STATUS_STATE_TRAN        (4lu << 9)
#define CARD_STATUS_STATE_DATA        (5lu << 9)
#define CARD_STATUS_STATE_RCV         (6lu << 9)
#define CARD_STATUS_STATE_PRG         (7lu << 9)
#define CARD_STATUS_STATE_DIS         (8lu << 9)
#define CARD_STATUS_STATE             (0xFlu << 9)
#define CARD_STATUS_ERASE_RESET       (1lu << 13)
#define CARD_STATUS_WP_ERASE_SKIP     (1lu << 15)
#define CARD_STATUS_CIDCSD_OVERWRITE  (1lu << 16)
#define CARD_STATUS_OVERRUN           (1lu << 17)
#define CARD_STATUS_UNERRUN           (1lu << 18)
#define CARD_STATUS_ERROR             (1lu << 19)
#define CARD_STATUS_CC_ERROR          (1lu << 20)
#define CARD_STATUS_CARD_ECC_FAILED   (1lu << 21)
#define CARD_STATUS_ILLEGAL_COMMAND   (1lu << 22)
#define CARD_STATUS_COM_CRC_ERROR     (1lu << 23)
#define CARD_STATUS_UNLOCK_FAILED     (1lu << 24)
#define CARD_STATUS_CARD_IS_LOCKED    (1lu << 25)
#define CARD_STATUS_WP_VIOLATION      (1lu << 26)
#define CARD_STATUS_ERASE_PARAM       (1lu << 27)
#define CARD_STATUS_ERASE_SEQ_ERROR   (1lu << 28)
#define CARD_STATUS_BLOCK_LEN_ERROR   (1lu << 29)
#define CARD_STATUS_ADDRESS_MISALIGN  (1lu << 30)
#define CARD_STATUS_ADDR_OUT_OF_RANGE (1lu << 31)

#define CARD_STATUS_ERR_RD_WR \
		( CARD_STATUS_ADDR_OUT_OF_RANGE \
		| CARD_STATUS_ADDRESS_MISALIGN \
		| CARD_STATUS_BLOCK_LEN_ERROR \
		| CARD_STATUS_WP_VIOLATION \
		| CARD_STATUS_ILLEGAL_COMMAND \
		| CARD_STATUS_CC_ERROR \
		| CARD_STATUS_ERROR)

#define SD_CMD_INIT (HSMCI_CMDR_RSPTYP_NORESP | HSMCI_CMDR_SPCMD_INIT | HSMCI_CMDR_OPDCMD_OPENDRAIN)
#define SD_CMD0 (HSMCI_CMDR_CMDNB(0) | HSMCI_CMDR_OPDCMD_OPENDRAIN)
#define SD_CMD2 (HSMCI_CMDR_CMDNB(2) | HSMCI_CMDR_RSPTYP_136_BIT | HSMCI_CMDR_OPDCMD_OPENDRAIN | HSMCI_CMDR_MAXLAT)
#define SD_CMD3 (HSMCI_CMDR_CMDNB(3) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_OPDCMD_OPENDRAIN | HSMCI_CMDR_MAXLAT)
#define SD_CMD4 (HSMCI_CMDR_CMDNB(4))
#define SD_CMD7 (HSMCI_CMDR_CMDNB(7) | HSMCI_CMDR_RSPTYP_R1B | HSMCI_CMDR_MAXLAT)
#define SD_CMD8 (HSMCI_CMDR_CMDNB(8) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_OPDCMD_OPENDRAIN | HSMCI_CMDR_MAXLAT)
#define SD_CMD9 (HSMCI_CMDR_CMDNB(9) | HSMCI_CMDR_RSPTYP_136_BIT | HSMCI_CMDR_MAXLAT)
#define SD_CMD13 (HSMCI_CMDR_CMDNB(13) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_MAXLAT)
#define SD_CMD16 (HSMCI_CMDR_CMDNB(16) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_MAXLAT)
#define SD_CMD17 (HSMCI_CMDR_CMDNB(17) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_MAXLAT | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_READ | HSMCI_CMDR_TRTYP_SINGLE)
#define SD_CMD24 (HSMCI_CMDR_CMDNB(24) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_MAXLAT | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_WRITE | HSMCI_CMDR_TRTYP_SINGLE)
#define SD_CMD55 (HSMCI_CMDR_CMDNB(55) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_MAXLAT)
#define SD_ACMD41 (HSMCI_CMDR_CMDNB(41) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_OPDCMD_OPENDRAIN | HSMCI_CMDR_MAXLAT)
#define SD_ACMD6 (HSMCI_CMDR_CMDNB(6) | HSMCI_CMDR_RSPTYP_48_BIT | HSMCI_CMDR_MAXLAT)

#define HSMCI_SR_ERROR (HSMCI_SR_RINDE | HSMCI_SR_RDIRE | HSMCI_SR_RCRCE | HSMCI_SR_RENDE | HSMCI_SR_RTOE | HSMCI_SR_DCRCE | HSMCI_SR_DTOE | HSMCI_SR_CSTOE | HSMCI_SR_BLKOVRE | HSMCI_SR_ACKRCVE | HSMCI_SR_OVRE | HSMCI_SR_UNRE)

SdHsmci _sd_hsmci;
HsmciParams _hsmci_params = {
	XDMA_HSMCI_TX_RX, 0
};

extern "C" {

// DMA interrupt

void SD_Handler(uint32_t channel) {
	HsmciParams *p = &_hsmci_params;
	if (XDMAC->XDMAC_CHID[p->dma_channel].XDMAC_CIS & XDMAC_CIS_BIS) {
		_xdma.Disable(p->dma_channel);
		p->owner->status = WAKE;
	}
}

// command interrupt

void HSMCI_Handler(void) {
	uint32_t status = HSMCI->HSMCI_SR;
	if (status & HSMCI_SR_CMDRDY & HSMCI->HSMCI_IMR) {
		HSMCI->HSMCI_IDR = HSMCI_IDR_CMDRDY;
		_hsmci_params.owner->status = WAKE;
	}
}

}

void SdHsmci::Init(uint32_t retries) {
	// configure the I/O lines
	// HSMCI uses the following pins:
	//   PA25 Peripheral D => MCCK
	//   PA26 Peripheral C => MCDA2
	//   PA27 Peripheral C => MCDA3
	//   PA28 Peripheral C => MCCDA
	//   PA30 Peripheral C => MCDA0
	//   PA31 Peripheral C => MCDA1
	PIO_DISABLE(PIOA, 0, 0xde000000);
	PIO_NO_PULL_UP(PIOA, 0, 0xde000000);
	PIO_CHOOSE_C(PIOA, 0, 0xdc000000);
	PIO_CHOOSE_D(PIOA, 0, 0x02000000);
	// enable the peripheral clock
	PMC->PMC_PCER0 = 1 << ID_HSMCI;
	// assign DMA channels
	HsmciParams *p = params_ = &_hsmci_params;
	if ((p->dma_channel = _xdma.GetAvailableChannel()) < 0) return;
	_xdma.SetHandler(p->dma_channel, SD_Handler);
	// before we initialize XDMAC, disable the HSMCI
	HSMCI->HSMCI_CR = HSMCI_CR_MCIDIS; // disable HSMCI
	HSMCI->HSMCI_IDR = ~0; // disable all interrupts
	_xdma.Disable(p->dma_channel);
	// setup HSMCI
	HSMCI->HSMCI_DTOR = HSMCI_DTOR_DTOMUL_1048576 | HSMCI_DTOR_DTOCYC(2); // set the data timeout register to 2 mega cycles
	HSMCI->HSMCI_CSTOR = HSMCI_CSTOR_CSTOMUL_1048576 | HSMCI_CSTOR_CSTOCYC(2); // set completion signal timeout to 2 mega cycles
	HSMCI->HSMCI_CFG =
		HSMCI_CFG_FIFOMODE |
		HSMCI_CFG_FERRCTRL;
	HSMCI->HSMCI_MR =
		HSMCI_MR_PWSDIV(0x7); // maximum power saving
	HSMCI->HSMCI_CR =
		HSMCI_CR_MCIEN | // enable HSMCI
		HSMCI_CR_PWSEN; // enable power saving
	// enable interrupts on HSMCI (we'll use an interrupt on CMDRDY to be notified when a command completes)
	NVIC_DisableIRQ(HSMCI_IRQn);
	NVIC_ClearPendingIRQ(HSMCI_IRQn);
	NVIC_SetPriority(HSMCI_IRQn, 0);
	NVIC_EnableIRQ(HSMCI_IRQn);
	// initialize the member variables
	retries_ = retries;
	error_ = 0;
}

// returns: 0 if no error
uint32_t SdHsmci::command(uint32_t cmd, uint32_t arg) {
	params_->owner = Active;
	HSMCI->HSMCI_SR;
	HSMCI->HSMCI_IER = HSMCI_IER_CMDRDY;
	HSMCI->HSMCI_ARGR = arg;
	HSMCI->HSMCI_CMDR = cmd;
	task::Stop(); // stop the calling task and wait for the command to complete
	return HSMCI->HSMCI_SR & HSMCI_SR_ERROR;
}

uint8_t SdHsmci::err(uint32_t error) {
	// we only have 8-bits to report errors
	return (error & HSMCI_SR_UNRE) >> 24 |
			   (error & HSMCI_SR_OVRE) >> 24 |
				 (error & HSMCI_SR_BLKOVRE) >> 19 |
				 (error & HSMCI_SR_CSTOE) >> 19 |
				 (error & HSMCI_SR_DTOE) >> 19 |
				 (error & HSMCI_SR_DCRCE) >> 19 |
				 (error & HSMCI_SR_RTOE) >> 19 |
				 (error & HSMCI_SR_RCRCE) >> 18;
}

void SdHsmci::set_speed(uint32_t speed) {
	uint32_t clkdiv;
	uint32_t clkodd;
	uint32_t div;
	// speed = MCK / (((clkdiv << 1) + clkodd) + 2)
	if ((speed * 2) < MCK) {
		div = (MCK / speed) - 2;
		if (MCK % speed) div++; // ensure that the card speed isn't higher than expected
		clkdiv = div >> 1;
		// clkodd is the last significant bit of the clock divider (div).
		clkodd = div % 2;
	} else {
		clkdiv = 0;
		clkodd = 0;
	}
	HSMCI->HSMCI_MR &= ~HSMCI_MR_CLKDIV_Msk;
	HSMCI->HSMCI_MR |= HSMCI_MR_CLKDIV(clkdiv);
	if (clkodd) HSMCI->HSMCI_MR |= HSMCI_MR_CLKODD;
	else HSMCI->HSMCI_MR &= ~HSMCI_MR_CLKODD;
}

uint32_t SdHsmci::Reset() {
	uint32_t r, resp;
	int32_t retries;
	HSMCI->HSMCI_CFG &= ~HSMCI_CFG_HSMODE;
	set_speed(400000); // clock must be set to 400000 kHz when initializing
	HSMCI->HSMCI_SDCR = HSMCI_SDCR_SDCBUS_1; // use 1 data lines during initialization
	HSMCI->HSMCI_MR &= ~(HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF | HSMCI_MR_FBYTE);
	HSMCI->HSMCI_DMA = 0; // disable DMA
	// send clock (at least 74 cycles)
	if ((r = command(SD_CMD_INIT, 0)) != 0) return error_ = 0x000 + err(r);
	// go idle
	HSMCI->HSMCI_BLKR = 0;
	if ((r = command(SD_CMD0, 0)) != 0) return error_ = 0x100 + err(r);
	// send interface condition (check if the card supports 2.7V-3.6V operation)
	retries = 10; // for some reason it takes a couple retries to get a valid response
	do {
		if ((r = command(SD_CMD8, 0x1aa)) != 0) return error_ = 0x200 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
	} while (--retries > 0 && (resp & 0xfff) != 0x1aa);
	// start card initialization
	retries = 2100; // timeout at 400 kHz => (6 + 6 + 6 + 6) * 8 = 2100
  //                                        ^   ^   ^   ^
	//                                        |   |   |   +- response byte size
	//                                        |   |   +----- cmd byte size
	//                                        |   +--------- response byte size
	//                                        +------------- cmd byte size
	do {
		if ((r = command(SD_CMD55, 0)) != 0) return error_ = 0x300 + err(r); // next command is application specific
		if ((r = command(SD_ACMD41, SD_ACMD41_HCS | SD_MMC_VOLTAGE_SUPPORT) & ~HSMCI_SR_RCRCE) != 0) return error_ = 0x400 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
		if (resp & OCR_POWER_UP_BUSY) {
			hc_ = (resp & OCR_CCS) != 0;
			break;
		}
	} while (retries-- > 0);
	if (retries < 0) return error_ = 0x500 + err(1); // timeout on busy
	// initialization complete
	// ask for CID number (put card in identify mode)
	if ((r = command(SD_CMD2, 0)) != 0) return error_ = 0x600 + err(r);
	// ask the card to send relative address
	if ((r = command(SD_CMD3, 0)) != 0) return error_ = 0x700 + err(r);
	resp = HSMCI->HSMCI_RSPR[0];
	rca_ = (resp >> 16) & 0xffff;
	if ((r = GetInfo()) != 0) return error_ = r;
	// select the card (we'll only select it once since we only have one card and we don't intend for the card to be replaced once initialized)
	if ((r = command(SD_CMD7, rca_ << 16)) != 0) return error_ = 0x800 + err(r);
	// set bus width to 4-bits
	if ((r = command(SD_CMD55, rca_ << 16)) != 0) return error_ = 0x900 + err(r); // next command is application specific
	if ((r = command(SD_ACMD6, 2)) != 0) return error_ = 0xa00 + err(r);
	set_speed(25000000); // we can also speed up the clock to full speed at this point
	HSMCI->HSMCI_SDCR = HSMCI_SDCR_SDCBUS_4; // now we can use 4 data lines
	// set block length
	if ((r = command(SD_CMD16, 512)) != 0) return error_ = 0xb00 + err(r);
	return 0;
}

uint32_t SdHsmci::GetInfo() {
	uint32_t r;
	if ((r = command(SD_CMD9, rca_ << 16)) != 0) return error_ = 0xc00 + err(r);
	csd_[0] = HSMCI->HSMCI_RSPR[0]; // bits 96-127
	csd_[1] = HSMCI->HSMCI_RSPR[1]; // bits 64-95
	csd_[2] = HSMCI->HSMCI_RSPR[2]; // bits 32-63
	csd_[3] = HSMCI->HSMCI_RSPR[3]; // bits 8-31
	return 0;
}

uint32_t SdHsmci::ReadBlock(char *buffer, uint32_t block) {
	uint32_t r, resp;
	int32_t retries;
	// wait for card status to indicate ready for data
	retries = 200000;
	do {
		if ((r = command(SD_CMD13, rca_ << 16)) != 0) return error_ = 0xd00 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
		if (resp & CARD_STATUS_READY_FOR_DATA) break;
		else task::Pause();
	} while (retries-- > 0);
	if (retries < 0) return error_ = 0xe00; // timeout occured
	HSMCI->HSMCI_DMA =
		HSMCI_DMA_DMAEN |
		HSMCI_DMA_CHKSIZE_16;
	// request a single block
	HSMCI->HSMCI_BLKR = HSMCI_BLKR_BLKLEN(512) | HSMCI_BLKR_BCNT(1);
	HSMCI->HSMCI_MR |= HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF;
	if ((r = command(SD_CMD17, block << (hc_ ? 0 : 9))) != 0) return error_ = 0xf00 + err(r);
	resp = HSMCI->HSMCI_RSPR[0];
	if (resp & CARD_STATUS_ERR_RD_WR) return error_ = 0x1000;
	// configure DMA to receive data
	_xdma.Configure(params_->dma_channel,
		XDMAC_CC_PERID(params_->dma_peripheral) |
		XDMAC_CC_SAM_FIXED_AM |
		XDMAC_CC_DAM_INCREMENTED_AM |
		XDMAC_CC_DWIDTH_WORD |
		XDMAC_CC_CSIZE_CHK_16 |
		XDMAC_CC_MEMSET_NORMAL_MODE |
		XDMAC_CC_SIF_AHB_IF1 |
		XDMAC_CC_DIF_AHB_IF0 |
		XDMAC_CC_DSYNC_PER2MEM |
		XDMAC_CC_MBSIZE_SINGLE |
		XDMAC_CC_TYPE_PER_TRAN,
		0,
		0
	);
	_xdma.Enable(params_->dma_channel, (uint32_t)&HSMCI->HSMCI_FIFO[0], (uint32_t)buffer, 128);
	// wait for the transfer to complete
	params_->owner = Active;
	task::Stop();
	return 0;
}

uint32_t SdHsmci::WriteBlock(char *buffer, uint32_t block) {
	uint32_t r, resp;
	int32_t retries;
	// wait for the card status to indicate ready for data
	retries = 200000;
	do {
		if ((r = command(SD_CMD13, rca_ << 16)) != 0) return error_ = 0x1100 + err(r);
		resp = HSMCI->HSMCI_RSPR[0];
		if (resp & CARD_STATUS_READY_FOR_DATA) break;
		else task::Pause();
	} while (retries-- > 0);
	if (retries < 0) return error_ = 0x1200; // timeout occured
	HSMCI->HSMCI_DMA =
		HSMCI_DMA_DMAEN |
		HSMCI_DMA_CHKSIZE_16;
	// write a single block
	HSMCI->HSMCI_BLKR = HSMCI_BLKR_BLKLEN(512) | HSMCI_BLKR_BCNT(1);
	HSMCI->HSMCI_MR |= HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF;
	if ((r = command(SD_CMD24, block << (hc_ ? 0 : 9))) != 0) return error_ = 0x1300 + err(r);
	resp = HSMCI->HSMCI_RSPR[0];
	if (resp & CARD_STATUS_ERR_RD_WR) return error_ = 0x1400 + err(r);
	// configure DMA to receive data
	_xdma.Configure(params_->dma_channel,
		XDMAC_CC_PERID(params_->dma_peripheral) |
		XDMAC_CC_DAM_FIXED_AM |
		XDMAC_CC_SAM_INCREMENTED_AM |
		XDMAC_CC_DWIDTH_WORD |
		XDMAC_CC_CSIZE_CHK_16 |
		XDMAC_CC_MEMSET_NORMAL_MODE |
		XDMAC_CC_SIF_AHB_IF0 |
		XDMAC_CC_DIF_AHB_IF1 |
		XDMAC_CC_DSYNC_MEM2PER |
		XDMAC_CC_MBSIZE_SINGLE |
		XDMAC_CC_TYPE_PER_TRAN,
		0,
		0
	);
	_xdma.Enable(params_->dma_channel, (uint32_t)buffer, (uint32_t)&HSMCI->HSMCI_FIFO[0], 128);
	// wait for the transfer to complete
	params_->owner = Active;
	task::Stop();
	return 0;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, regarding the data transfer rate: 25 MHz * 4 data lines / 8 bits/byte = 12.5 MB/s

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

how i will get this project. i want to see HSMCI card as a removable disk with SAME70 can u help me to find it.