This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
reprapfirmware-dc42/Libraries/SD_HSMCI/utility/hsmci.c
David Crocker efe13ef2f1 Version 1.09k
Added F (probing speed) and T (travel speed) parameters to M558 command
Removed M210 command because home feed rates are defined in the homing
files
Increased UART interrupt priority to avoid dropping characters sent by
PanelDue
Bug fix: M558 P3 did not leave the Z probe control pin high
Bug fix: in version 1.09j only, the move following a G92 E0 command was
sometimes executed from an incorrect start point
Fixed bugs with reads/writes from/to the SD card that spanned one or
more whole sectors
Updated to latest Atmel HSMCI driver
2015-09-20 15:14:00 +01:00

911 lines
24 KiB
C

/**
* \file
*
* \brief SAM HSMCI driver
*
* Copyright (c) 2012-2015 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#include "../SD_HSMCI.h"
#include "sd_mmc_protocol.h"
#include "pmc.h"
#include "hsmci.h"
/**
* \ingroup sam_drivers_hsmci
* \defgroup sam_drivers_hsmci_internal High Speed MultiMedia Card Interface
* (HSMCI) implementation
*
* @{
*/
// Check configurations
#if (!defined SD_MMC_HSMCI_MEM_CNT) || (SD_MMC_HSMCI_MEM_CNT == 0)
# warning SD_MMC_HSMCI_MEM_CNT must be defined in board.h file.
# define SD_MMC_HSMCI_MEM_CNT 1
#endif
#ifndef CONF_BOARD_SD_MMC_HSMCI
# warning CONF_BOARD_SD_MMC_HSMCI must be defined in conf_board.h file.
#endif
#if (SAM3XA)
# if (SD_MMC_HSMCI_MEM_CNT > 2)
# warning Wrong define SD_MMC_HSMCI_MEM_CNT in board.h,\
this part have 2 slots maximum on HSMCI.
# endif
#else
# if (SD_MMC_HSMCI_MEM_CNT > 1)
# warning Wrong define SD_MMC_HSMCI_MEM_CNT in board.h,\
this part have 1 slots maximum on HSMCI.
# endif
#endif
#ifndef SD_MMC_HSMCI_SLOT_0_SIZE
# warning SD_MMC_HSMCI_SLOT_0_SIZE must be defined in board.h.
# define SD_MMC_HSMCI_SLOT_0_SIZE 1
#endif
#if (SD_MMC_HSMCI_MEM_CNT > 2)
# ifndef SD_MMC_HSMCI_SLOT_1_SIZE
# warning SD_MMC_HSMCI_SLOT_1_SIZE must be defined in board.h.
# define SD_MMC_HSMCI_SLOT_1_SIZE 1
# endif
#endif
// Enable debug information for SD/MMC SPI module
#ifdef HSMCI_DEBUG
# define hsmci_debug(...) debugPrintf(__VA_ARGS__)
#else
# define hsmci_debug(...)
#endif
#if (SAM3S || SAM4S || SAM4E)
// PDC is used for transfer
#elif (SAM3U || SAM3XA || SAM3XE)
// DMA is used for transfer
# include "dmac.h"
# define DMA_HW_ID_HSMCI 0
# ifndef CONF_HSMCI_DMA_CHANNEL
# define CONF_HSMCI_DMA_CHANNEL 0
# endif
#else
# error Not supported device
#endif
//! Current position (byte) of the transfer started by hsmci_adtc_start()
static uint32_t hsmci_transfert_pos;
//! Size block requested by last hsmci_adtc_start()
static uint16_t hsmci_block_size;
//! Total number of block requested by last hsmci_adtc_start()
static uint16_t hsmci_nb_block;
static void hsmci_reset(void);
static void hsmci_set_speed(uint32_t speed, uint32_t mck);
static bool hsmci_wait_busy(void);
static bool hsmci_send_cmd_execute(uint32_t cmdr, sdmmc_cmd_def_t cmd,
uint32_t arg);
/**
* \brief Reset the HSMCI interface
*/
static void hsmci_reset(void)
{
uint32_t mr = HSMCI->HSMCI_MR;
uint32_t dtor = HSMCI->HSMCI_DTOR;
uint32_t sdcr = HSMCI->HSMCI_SDCR;
uint32_t cstor = HSMCI->HSMCI_CSTOR;
uint32_t cfg = HSMCI->HSMCI_CFG;
HSMCI->HSMCI_CR = HSMCI_CR_SWRST;
HSMCI->HSMCI_MR = mr;
HSMCI->HSMCI_DTOR = dtor;
HSMCI->HSMCI_SDCR = sdcr;
HSMCI->HSMCI_CSTOR = cstor;
HSMCI->HSMCI_CFG = cfg;
#ifdef HSMCI_SR_DMADONE
HSMCI->HSMCI_DMA = 0;
#endif
// Enable the HSMCI
HSMCI->HSMCI_CR = HSMCI_CR_PWSEN | HSMCI_CR_MCIEN;
}
/**
* \brief Set speed of the HSMCI clock.
*
* \param speed HSMCI clock speed in Hz.
* \param mck MCK clock speed in Hz.
*/
static void hsmci_set_speed(uint32_t speed, uint32_t mck)
{
#if (SAM4E)
uint32_t clkdiv = 0;
uint32_t clkodd = 0;
// clock divider, represent (((clkdiv << 1) + clkodd) + 2)
uint32_t div = 0;
// Speed = MCK clock / (((clkdiv << 1) + clkodd) + 2)
if ((speed * 2) < mck) {
div = (mck / speed) - 2;
if (mck % speed) {
// Ensure that the card speed not be higher than expected.
div++;
}
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;
}
#else
uint32_t clkdiv = 0;
uint32_t rest = 0;
// Speed = MCK clock / (2 * (CLKDIV + 1))
if ((speed * 2) < mck) {
clkdiv = mck / (2 * speed);
rest = mck % (2 * speed);
if (rest > 0) {
// Ensure that the card speed not be higher than expected.
clkdiv++;
}
if (clkdiv > 0) {
clkdiv -= 1;
}
} else {
clkdiv = 0;
}
HSMCI->HSMCI_MR &= ~HSMCI_MR_CLKDIV_Msk;
HSMCI->HSMCI_MR |= HSMCI_MR_CLKDIV(clkdiv);
#endif
}
/** \brief Wait the end of busy signal on data line
*
* \return true if success, otherwise false
*/
static bool hsmci_wait_busy(void)
{
uint32_t busy_wait = 0xFFFFFFFF;
uint32_t sr;
do {
sr = HSMCI->HSMCI_SR;
if (busy_wait-- == 0) {
hsmci_debug("%s: timeout\n\r", __func__);
hsmci_reset();
return false;
}
} while (!((sr & HSMCI_SR_NOTBUSY) && ((sr & HSMCI_SR_DTIP) == 0)));
return true;
}
/** \brief Send a command
*
* \param cmdr CMDR register bit to use for this command
* \param cmd Command definition
* \param arg Argument of the command
*
* \return true if success, otherwise false
*/
static bool hsmci_send_cmd_execute(uint32_t cmdr, sdmmc_cmd_def_t cmd,
uint32_t arg)
{
uint32_t sr;
cmdr |= HSMCI_CMDR_CMDNB(cmd) | HSMCI_CMDR_SPCMD_STD;
if (cmd & SDMMC_RESP_PRESENT) {
cmdr |= HSMCI_CMDR_MAXLAT;
if (cmd & SDMMC_RESP_136) {
cmdr |= HSMCI_CMDR_RSPTYP_136_BIT;
} else if (cmd & SDMMC_RESP_BUSY) {
cmdr |= HSMCI_CMDR_RSPTYP_R1B;
} else {
cmdr |= HSMCI_CMDR_RSPTYP_48_BIT;
}
}
if (cmd & SDMMC_CMD_OPENDRAIN) {
cmdr |= HSMCI_CMDR_OPDCMD_OPENDRAIN;
}
// Write argument
HSMCI->HSMCI_ARGR = arg;
// Write and start command
HSMCI->HSMCI_CMDR = cmdr;
// Wait end of command
do {
sr = HSMCI->HSMCI_SR;
if (cmd & SDMMC_RESP_CRC) {
if (sr & (HSMCI_SR_CSTOE | HSMCI_SR_RTOE
| HSMCI_SR_RENDE | HSMCI_SR_RCRCE
| HSMCI_SR_RDIRE | HSMCI_SR_RINDE)) {
hsmci_debug("%s: CMD 0x%08x sr 0x%08x error\n\r",
__func__, cmd, sr);
hsmci_reset();
return false;
}
} else {
if (sr & (HSMCI_SR_CSTOE | HSMCI_SR_RTOE
| HSMCI_SR_RENDE
| HSMCI_SR_RDIRE | HSMCI_SR_RINDE)) {
hsmci_debug("%s: CMD 0x%08x sr 0x%08x error\n\r",
__func__, cmd, sr);
hsmci_reset();
return false;
}
}
} while (!(sr & HSMCI_SR_CMDRDY));
if (cmd & SDMMC_RESP_BUSY) {
if (!hsmci_wait_busy()) {
return false;
}
}
return true;
}
//-------------------------------------------------------------------
//--------------------- PUBLIC FUNCTIONS ----------------------------
void hsmci_init(void)
{
pmc_enable_periph_clk(ID_HSMCI);
#ifdef HSMCI_SR_DMADONE
// Enable clock for DMA controller
pmc_enable_periph_clk(ID_DMAC);
#endif
// Set the Data Timeout Register to 2 Mega Cycles
HSMCI->HSMCI_DTOR = HSMCI_DTOR_DTOMUL_1048576 | HSMCI_DTOR_DTOCYC(2);
// Set Completion Signal Timeout to 2 Mega Cycles
HSMCI->HSMCI_CSTOR = HSMCI_CSTOR_CSTOMUL_1048576 | HSMCI_CSTOR_CSTOCYC(2);
// Set Configuration Register
HSMCI->HSMCI_CFG = HSMCI_CFG_FIFOMODE | HSMCI_CFG_FERRCTRL;
// Set power saving to maximum value
HSMCI->HSMCI_MR = HSMCI_MR_PWSDIV_Msk;
// Enable the HSMCI and the Power Saving
HSMCI->HSMCI_CR = HSMCI_CR_MCIEN | HSMCI_CR_PWSEN;
}
uint8_t hsmci_get_bus_width(uint8_t slot)
{
switch (slot) {
case 0:
return SD_MMC_HSMCI_SLOT_0_SIZE;
#if (SD_MMC_HSMCI_MEM_CNT == 2)
case 1:
return SD_MMC_HSMCI_SLOT_1_SIZE;
#endif
default:
return 0; // Slot number wrong
}
}
bool hsmci_is_high_speed_capable(void)
{
return true;
}
void hsmci_select_device(uint8_t slot, uint32_t clock, uint8_t bus_width, bool high_speed)
{
uint32_t hsmci_slot = HSMCI_SDCR_SDCSEL_SLOTA;
uint32_t hsmci_bus_width = HSMCI_SDCR_SDCBUS_1;
if (high_speed) {
HSMCI->HSMCI_CFG |= HSMCI_CFG_HSMODE;
} else {
HSMCI->HSMCI_CFG &= ~HSMCI_CFG_HSMODE;
}
hsmci_set_speed(clock, sysclk_get_cpu_hz());
switch (slot) {
case 0:
hsmci_slot = HSMCI_SDCR_SDCSEL_SLOTA;
break;
#if (SD_MMC_HSMCI_MEM_CNT == 2)
case 1:
hsmci_slot = HSMCI_SDCR_SDCSEL_SLOTB;
break;
#endif
default:
Assert(false); // Slot number wrong
}
switch (bus_width) {
case 1:
hsmci_bus_width = HSMCI_SDCR_SDCBUS_1;
break;
case 4:
hsmci_bus_width = HSMCI_SDCR_SDCBUS_4;
break;
case 8:
hsmci_bus_width = HSMCI_SDCR_SDCBUS_8;
break;
default:
Assert(false); // Bus width wrong
}
HSMCI->HSMCI_SDCR = hsmci_slot | hsmci_bus_width;
}
void hsmci_deselect_device(uint8_t slot)
{
UNUSED(slot);
// Nothing to do
}
void hsmci_send_clock(void)
{
// Configure command
HSMCI->HSMCI_MR &= ~(HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF | HSMCI_MR_FBYTE);
// Write argument
HSMCI->HSMCI_ARGR = 0;
// Write and start initialization command
HSMCI->HSMCI_CMDR = HSMCI_CMDR_RSPTYP_NORESP
| HSMCI_CMDR_SPCMD_INIT
| HSMCI_CMDR_OPDCMD_OPENDRAIN;
// Wait end of initialization command
while (!(HSMCI->HSMCI_SR & HSMCI_SR_CMDRDY));
}
bool hsmci_send_cmd(sdmmc_cmd_def_t cmd, uint32_t arg)
{
// Configure command
HSMCI->HSMCI_MR &= ~(HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF | HSMCI_MR_FBYTE);
#ifdef HSMCI_SR_DMADONE
// Disable DMA for HSMCI
HSMCI->HSMCI_DMA = 0;
#endif
#ifdef HSMCI_MR_PDCMODE
// Disable PDC for HSMCI
HSMCI->HSMCI_MR &= ~HSMCI_MR_PDCMODE;
#endif
HSMCI->HSMCI_BLKR = 0;
return hsmci_send_cmd_execute(0, cmd, arg);
}
uint32_t hsmci_get_response(void)
{
return HSMCI->HSMCI_RSPR[0];
}
void hsmci_get_response_128(uint8_t* response)
{
uint32_t response_32;
for (uint8_t i = 0; i < 4; i++) {
response_32 = HSMCI->HSMCI_RSPR[0];
*response = (response_32 >> 24) & 0xFF;
response++;
*response = (response_32 >> 16) & 0xFF;
response++;
*response = (response_32 >> 8) & 0xFF;
response++;
*response = (response_32 >> 0) & 0xFF;
response++;
}
}
bool hsmci_adtc_start(sdmmc_cmd_def_t cmd, uint32_t arg, uint16_t block_size, uint16_t nb_block, bool access_block)
{
uint32_t cmdr;
#ifdef HSMCI_SR_DMADONE
if (access_block) {
// Enable DMA for HSMCI
HSMCI->HSMCI_DMA = HSMCI_DMA_DMAEN;
} else {
// Disable DMA for HSMCI
HSMCI->HSMCI_DMA = 0;
}
#endif
#ifdef HSMCI_MR_PDCMODE
if (access_block) {
// Enable PDC for HSMCI
HSMCI->HSMCI_MR |= HSMCI_MR_PDCMODE;
} else {
// Disable PDC for HSMCI
HSMCI->HSMCI_MR &= ~HSMCI_MR_PDCMODE;
}
#endif
// Enabling Read/Write Proof allows to stop the HSMCI Clock during
// read/write access if the internal FIFO is full.
// This will guarantee data integrity, not bandwidth.
HSMCI->HSMCI_MR |= HSMCI_MR_WRPROOF | HSMCI_MR_RDPROOF;
// Force byte transfer if needed
if (block_size & 0x3) {
HSMCI->HSMCI_MR |= HSMCI_MR_FBYTE;
} else {
HSMCI->HSMCI_MR &= ~HSMCI_MR_FBYTE;
}
if (cmd & SDMMC_CMD_WRITE) {
cmdr = HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_WRITE;
} else {
cmdr = HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_READ;
}
if (cmd & SDMMC_CMD_SDIO_BYTE) {
cmdr |= HSMCI_CMDR_TRTYP_BYTE;
// Value 0 corresponds to a 512-byte transfer
HSMCI->HSMCI_BLKR = ((block_size % 512) << HSMCI_BLKR_BCNT_Pos);
} else {
HSMCI->HSMCI_BLKR = (block_size << HSMCI_BLKR_BLKLEN_Pos) |
(nb_block << HSMCI_BLKR_BCNT_Pos);
if (cmd & SDMMC_CMD_SDIO_BLOCK) {
cmdr |= HSMCI_CMDR_TRTYP_BLOCK;
} else if (cmd & SDMMC_CMD_STREAM) {
cmdr |= HSMCI_CMDR_TRTYP_STREAM;
} else if (cmd & SDMMC_CMD_SINGLE_BLOCK) {
cmdr |= HSMCI_CMDR_TRTYP_SINGLE;
} else if (cmd & SDMMC_CMD_MULTI_BLOCK) {
cmdr |= HSMCI_CMDR_TRTYP_MULTIPLE;
} else {
Assert(false); // Incorrect flags
}
}
hsmci_transfert_pos = 0;
hsmci_block_size = block_size;
hsmci_nb_block = nb_block;
return hsmci_send_cmd_execute(cmdr, cmd, arg);
}
bool hsmci_adtc_stop(sdmmc_cmd_def_t cmd, uint32_t arg)
{
return hsmci_send_cmd_execute(HSMCI_CMDR_TRCMD_STOP_DATA, cmd, arg);
}
bool hsmci_read_word(uint32_t* value)
{
uint32_t sr;
Assert(((uint32_t)hsmci_block_size * hsmci_nb_block) > hsmci_transfert_pos);
// Wait data available
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: DMA sr 0x%08x error\n\r",
__func__, sr);
hsmci_reset();
return false;
}
} while (!(sr & HSMCI_SR_RXRDY));
// Read data
*value = HSMCI->HSMCI_RDR;
hsmci_transfert_pos += 4;
if (((uint32_t)hsmci_block_size * hsmci_nb_block) > hsmci_transfert_pos) {
return true;
}
// Wait end of transfer
// Note: no need of timeout, because it is include in HSMCI
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: DMA sr 0x%08x error\n\r",
__func__, sr);
hsmci_reset();
return false;
}
} while (!(sr & HSMCI_SR_XFRDONE));
return true;
}
bool hsmci_write_word(uint32_t value)
{
uint32_t sr;
Assert(((uint32_t)hsmci_block_size * hsmci_nb_block) > hsmci_transfert_pos);
// Wait data available
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: DMA sr 0x%08x error\n\r",
__func__, sr);
hsmci_reset();
return false;
}
} while (!(sr & HSMCI_SR_TXRDY));
// Write data
HSMCI->HSMCI_TDR = value;
hsmci_transfert_pos += 4;
if (((uint32_t)hsmci_block_size * hsmci_nb_block) > hsmci_transfert_pos) {
return true;
}
// Wait end of transfer
// Note: no need of timeout, because it is include in HSMCI, see DTOE bit.
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: DMA sr 0x%08x error\n\r",
__func__, sr);
hsmci_reset();
return false;
}
} while (!(sr & HSMCI_SR_NOTBUSY));
Assert(HSMCI->HSMCI_SR & HSMCI_SR_FIFOEMPTY);
return true;
}
#ifdef HSMCI_SR_DMADONE
// Debug variables
//uint32_t longestWriteWaitTime=0, shortestWriteWaitTime=999999, longestReadWaitTime=0, shortestReadWaitTime=999999, numRead=0, numWrite=0;
bool hsmci_start_read_blocks(void *dest, uint16_t nb_block)
{
uint32_t cfg, nb_data;
dma_transfer_descriptor_t desc;
bool transfert_byte;
nb_data = nb_block * hsmci_block_size;
transfert_byte = ((HSMCI->HSMCI_MR & HSMCI_MR_FBYTE) || (((uint32_t)dest & 0x3) > 0)) ? 1 : 0;
Assert(nb_data <= (((uint32_t)hsmci_block_size * hsmci_nb_block) - hsmci_transfert_pos));
Assert(nb_data <= (transfert_byte ?
DMAC_CTRLA_BTSIZE_Msk >> DMAC_CTRLA_BTSIZE_Pos :
((DMAC_CTRLA_BTSIZE_Msk >> DMAC_CTRLA_BTSIZE_Pos) * 4)));
/* Set channel configuration register
* - Enable stop on done
* - Hardware Selection for the Source
* - Source with Peripheral identifier
* - Set AHB Protection
* - FIFO Configuration
*/
dmac_enable(DMAC);
dmac_channel_disable(DMAC, CONF_HSMCI_DMA_CHANNEL);
cfg = DMAC_CFG_SOD_ENABLE | DMAC_CFG_SRC_H2SEL |
DMAC_CFG_SRC_PER(DMA_HW_ID_HSMCI) |
DMAC_CFG_AHB_PROT(1) | DMAC_CFG_FIFOCFG_ALAP_CFG;
dmac_channel_set_configuration(DMAC, CONF_HSMCI_DMA_CHANNEL, cfg);
// Prepare DMA transfer
desc.ul_source_addr = (uint32_t)&(HSMCI->HSMCI_RDR);
desc.ul_destination_addr = (uint32_t)dest;
if (transfert_byte) {
desc.ul_ctrlA = DMAC_CTRLA_BTSIZE(nb_data)
| DMAC_CTRLA_SRC_WIDTH_BYTE
| DMAC_CTRLA_DST_WIDTH_BYTE;
} else {
desc.ul_ctrlA = DMAC_CTRLA_BTSIZE(nb_data / 4)
| DMAC_CTRLA_SRC_WIDTH_WORD
| DMAC_CTRLA_DST_WIDTH_WORD;
}
desc.ul_ctrlB = DMAC_CTRLB_SRC_DSCR_FETCH_DISABLE
| DMAC_CTRLB_DST_DSCR_FETCH_DISABLE
| DMAC_CTRLB_FC_PER2MEM_DMA_FC
| DMAC_CTRLB_SRC_INCR_FIXED
| DMAC_CTRLB_DST_INCR_INCREMENTING
| DMAC_CTRLB_IEN;
desc.ul_descriptor_addr = (uint32_t)NULL;
dmac_channel_single_buf_transfer_init(DMAC, CONF_HSMCI_DMA_CHANNEL,
&desc);
// Start DMA transfer
dmac_channel_enable(DMAC, CONF_HSMCI_DMA_CHANNEL);
hsmci_transfert_pos += nb_data;
return true;
}
bool hsmci_wait_end_of_read_blocks(void)
{
//debug
//uint32_t time = micros(); ++numRead;
//
uint32_t sr;
// Wait end of transfer
// Note: no need of timeout, because it is include in HSMCI
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: DMA sr 0x%08x error\n\r",
__func__, sr);
hsmci_reset();
// Disable DMA
dmac_channel_disable(DMAC, CONF_HSMCI_DMA_CHANNEL);
return false;
}
if (((uint32_t)hsmci_block_size * hsmci_nb_block) > hsmci_transfert_pos) {
// It is not the end of all transfers
// then just wait end of DMA
if (sr & HSMCI_SR_DMADONE) {
//time = micros() - time;
//if (time > longestReadWaitTime) { longestReadWaitTime = time; }
//if (time < shortestReadWaitTime) { shortestReadWaitTime = time; }
return true;
}
}
} while (!(sr & HSMCI_SR_XFRDONE));
//time = micros() - time;
//if (time > longestReadWaitTime) { longestReadWaitTime = time; }
//if (time < shortestReadWaitTime) { shortestReadWaitTime = time; }
return true;
}
bool hsmci_start_write_blocks(const void *src, uint16_t nb_block)
{
bool transfert_byte;
uint32_t cfg, nb_data;
dma_transfer_descriptor_t desc;
nb_data = nb_block * hsmci_block_size;
transfert_byte = ((HSMCI->HSMCI_MR & HSMCI_MR_FBYTE) || (((uint32_t)src & 0x3) > 0)) ? 1 : 0;
Assert(nb_data <= (((uint32_t)hsmci_block_size * hsmci_nb_block) - hsmci_transfert_pos));
Assert(nb_data <= (transfert_byte ?
DMAC_CTRLA_BTSIZE_Msk >> DMAC_CTRLA_BTSIZE_Pos :
((DMAC_CTRLA_BTSIZE_Msk >> DMAC_CTRLA_BTSIZE_Pos) * 4)));
/* Set channel configuration register:
* - Enable stop on done
* - Hardware Selection for the Destination
* - Destination with Peripheral identifier
* - Set AHB Protection
* - FIFO Configuration
*/
dmac_enable(DMAC);
Assert(!dmac_channel_is_enable(DMAC, CONF_HSMCI_DMA_CHANNEL));
cfg = DMAC_CFG_SOD_ENABLE | DMAC_CFG_DST_H2SEL |
DMAC_CFG_DST_PER(DMA_HW_ID_HSMCI) |
DMAC_CFG_AHB_PROT(1) | DMAC_CFG_FIFOCFG_ALAP_CFG;
dmac_channel_set_configuration(DMAC, CONF_HSMCI_DMA_CHANNEL, cfg);
// Prepare DMA transfer
desc.ul_source_addr = (uint32_t)src;
desc.ul_destination_addr = (uint32_t)&(HSMCI->HSMCI_TDR);
if (transfert_byte) {
desc.ul_ctrlA = DMAC_CTRLA_BTSIZE(nb_data)
| DMAC_CTRLA_SRC_WIDTH_BYTE
| DMAC_CTRLA_DST_WIDTH_BYTE;
} else {
desc.ul_ctrlA = DMAC_CTRLA_BTSIZE(nb_data / 4)
| DMAC_CTRLA_SRC_WIDTH_WORD
| DMAC_CTRLA_DST_WIDTH_WORD;
}
desc.ul_ctrlB = DMAC_CTRLB_SRC_DSCR_FETCH_DISABLE
| DMAC_CTRLB_DST_DSCR_FETCH_DISABLE
| DMAC_CTRLB_FC_MEM2PER_DMA_FC
| DMAC_CTRLB_SRC_INCR_INCREMENTING
| DMAC_CTRLB_DST_INCR_FIXED
| DMAC_CTRLB_IEN;
desc.ul_descriptor_addr = (uint32_t)NULL;
dmac_channel_single_buf_transfer_init(DMAC, CONF_HSMCI_DMA_CHANNEL,
&desc);
// Start DMA transfer
dmac_channel_enable(DMAC, CONF_HSMCI_DMA_CHANNEL);
hsmci_transfert_pos += nb_data;
return true;
}
bool hsmci_wait_end_of_write_blocks(void)
{
//debug
//uint32_t time = micros(); ++numWrite;
//
uint32_t sr;
// Wait end of transfer
// Note: no need of timeout, because it is include in HSMCI, see DTOE bit.
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: DMA sr 0x%08x error\n\r",
__func__, sr);
hsmci_reset();
// Disable DMA
dmac_channel_disable(DMAC, CONF_HSMCI_DMA_CHANNEL);
return false;
}
if (((uint32_t)hsmci_block_size * hsmci_nb_block) > hsmci_transfert_pos) {
// It is not the end of all transfers
// then just wait end of DMA
if (sr & HSMCI_SR_DMADONE) {
//time = micros() - time;
//if (time > longestWriteWaitTime) { longestWriteWaitTime = time; }
//if (time < shortestWriteWaitTime) { shortestWriteWaitTime = time; }
return true;
}
}
} while (!(sr & HSMCI_SR_NOTBUSY));
Assert(HSMCI->HSMCI_SR & HSMCI_SR_FIFOEMPTY);
Assert(!dmac_channel_is_enable(DMAC, CONF_HSMCI_DMA_CHANNEL));
//time = micros() - time;
//if (time > longestWriteWaitTime) { longestWriteWaitTime = time; }
//if (time < shortestWriteWaitTime) { shortestWriteWaitTime = time; }
return true;
}
#endif // HSMCI_SR_DMADONE
#ifdef HSMCI_MR_PDCMODE
bool hsmci_start_read_blocks(void *dest, uint16_t nb_block)
{
uint32_t nb_data;
nb_data = nb_block * hsmci_block_size;
Assert(nb_data <= (((uint32_t)hsmci_block_size * hsmci_nb_block) - hsmci_transfert_pos));
Assert(nb_data <= (PERIPH_RCR_RXCTR_Msk >> PERIPH_RCR_RXCTR_Pos));
// Handle unaligned memory address
if (((uint32_t)dest & 0x3) || (hsmci_block_size & 0x3)) {
HSMCI->HSMCI_MR |= HSMCI_MR_FBYTE;
} else {
HSMCI->HSMCI_MR &= ~HSMCI_MR_FBYTE;
}
// Configure PDC transfer
HSMCI->HSMCI_RPR = (uint32_t)dest;
HSMCI->HSMCI_RCR = (HSMCI->HSMCI_MR & HSMCI_MR_FBYTE) ?
nb_data : nb_data / 4;
HSMCI->HSMCI_RNCR = 0;
// Start transfer
HSMCI->HSMCI_PTCR = HSMCI_PTCR_RXTEN;
hsmci_transfert_pos += nb_data;
return true;
}
bool hsmci_wait_end_of_read_blocks(void)
{
uint32_t sr;
// Wait end of transfer
// Note: no need of timeout, because it is include in HSMCI, see DTOE bit.
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: PDC sr 0x%08x error\n\r",
__func__, sr);
HSMCI->HSMCI_PTCR = HSMCI_PTCR_RXTDIS | HSMCI_PTCR_TXTDIS;
hsmci_reset();
return false;
}
} while (!(sr & HSMCI_SR_RXBUFF));
if (hsmci_transfert_pos < ((uint32_t)hsmci_block_size * hsmci_nb_block)) {
return true;
}
// It is the last transfer, then wait command completed
// Note: no need of timeout, because it is include in HSMCI, see DTOE bit.
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: PDC sr 0x%08x last transfer error\n\r",
__func__, sr);
hsmci_reset();
return false;
}
} while (!(sr & HSMCI_SR_XFRDONE));
return true;
}
bool hsmci_start_write_blocks(const void *src, uint16_t nb_block)
{
uint32_t nb_data;
nb_data = nb_block * hsmci_block_size;
Assert(nb_data <= (((uint32_t)hsmci_block_size * hsmci_nb_block) - hsmci_transfert_pos));
Assert(nb_data <= (PERIPH_TCR_TXCTR_Msk >> PERIPH_TCR_TXCTR_Pos));
// Handle unaligned memory address
if (((uint32_t)src & 0x3) || (hsmci_block_size & 0x3)) {
HSMCI->HSMCI_MR |= HSMCI_MR_FBYTE;
} else {
HSMCI->HSMCI_MR &= ~HSMCI_MR_FBYTE;
}
// Configure PDC transfer
HSMCI->HSMCI_TPR = (uint32_t)src;
HSMCI->HSMCI_TCR = (HSMCI->HSMCI_MR & HSMCI_MR_FBYTE) ?
nb_data : nb_data / 4;
HSMCI->HSMCI_TNCR = 0;
// Start transfer
HSMCI->HSMCI_PTCR = HSMCI_PTCR_TXTEN;
hsmci_transfert_pos += nb_data;
return true;
}
bool hsmci_wait_end_of_write_blocks(void)
{
uint32_t sr;
// Wait end of transfer
// Note: no need of timeout, because it is include in HSMCI, see DTOE bit.
do {
sr = HSMCI->HSMCI_SR;
if (sr &
(HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: PDC sr 0x%08x error\n\r",
__func__, sr);
hsmci_reset();
HSMCI->HSMCI_PTCR = HSMCI_PTCR_RXTDIS | HSMCI_PTCR_TXTDIS;
return false;
}
} while (!(sr & HSMCI_SR_TXBUFE));
if (hsmci_transfert_pos < ((uint32_t)hsmci_block_size * hsmci_nb_block)) {
return true;
}
// It is the last transfer, then wait command completed
// Note: no need of timeout, because it is include in HSMCI, see DTOE bit.
do {
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
hsmci_debug("%s: PDC sr 0x%08x last transfer error\n\r",
__func__, sr);
hsmci_reset();
return false;
}
} while (!(sr & HSMCI_SR_NOTBUSY));
Assert(HSMCI->HSMCI_SR & HSMCI_SR_FIFOEMPTY);
return true;
}
#endif // HSMCI_MR_PDCMODE