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 0c85453ace Added libs; prepare network startup change
Added the libraries to the repository
Preliminary changes to fix slow startup when no network cable is
connected
2014-04-18 21:06:16 +01:00

834 lines
22 KiB
C

/**
* \file
*
* \brief SAM HSMCI driver
*
* Copyright (c) 2012 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
*
*/
#include "../SD_HSMCI.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_SERIES)
# 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 4
#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
#if (SAM3S || SAM4S)
// PDC is used for transferts
#elif (SAM3U || SAM3XA_SERIES)
// DMA is used for transferts
# 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)
{
uint32_t clkdiv;
uint32_t rest;
// Speed = MCK clock / (2 * (CLKDIV + 1))
if (speed > 0) {
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);
}
/** \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 = 1000000;
uint32_t sr;
do {
sr = HSMCI->HSMCI_SR;
if (busy_wait-- == 0) {
// printf("%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 resgister 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 == 0x5103) {
if (sr & (HSMCI_SR_CSTOE | HSMCI_SR_RTOE
| HSMCI_SR_RCRCE
| HSMCI_SR_RDIRE)) {
// printf("%s: CMD 0x%08x sr 0x%08x CMD 3 error\n\r",__func__, cmd, sr);
hsmci_reset();
return false;
}
} else 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)) {
// printf("%s: CMD 0x%08x sr 0x%08x RESP_CRC 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)) {
// printf("%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, SystemCoreClock);
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
break;
}
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
break;
}
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;
uint8_t i=0;
for (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)) {
// printf("%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)) {
// printf("%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)) {
// printf("%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)) {
// printf("%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
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)
{
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)) {
// printf("%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) {
return true;
}
}
} while (!(sr & HSMCI_SR_XFRDONE));
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)
{
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)) {
//printf("%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) {
return true;
}
}
} while (!(sr & HSMCI_SR_NOTBUSY));
Assert(HSMCI->HSMCI_SR & HSMCI_SR_FIFOEMPTY);
Assert(!dmac_channel_is_enable(DMAC, CONF_HSMCI_DMA_CHANNEL));
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));
// Configure PDC transfert
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 transfert
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 transfert
// 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)) {
//printf("%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)) {
//printf("%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));
// Configure PDC transfert
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 transfert
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 transfert
// 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)) {
//printf("%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)) {
//printf("%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