From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- peripherals/mci/mci.c | 712 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 712 insertions(+) create mode 100644 peripherals/mci/mci.c (limited to 'peripherals/mci/mci.c') diff --git a/peripherals/mci/mci.c b/peripherals/mci/mci.c new file mode 100644 index 0000000..d829f85 --- /dev/null +++ b/peripherals/mci/mci.c @@ -0,0 +1,712 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: 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 + * 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "mci.h" +#include +#include + +//------------------------------------------------------------------------------ +// Local constants +//------------------------------------------------------------------------------ + +/// Bit mask for status register errors. +#define STATUS_ERRORS ((unsigned int)(AT91C_MCI_UNRE \ + | AT91C_MCI_OVRE \ + | AT91C_MCI_DTOE \ + | AT91C_MCI_DCRCE \ + | AT91C_MCI_RTOE \ + | AT91C_MCI_RENDE \ + | AT91C_MCI_RCRCE \ + | AT91C_MCI_RDIRE \ + | AT91C_MCI_RINDE)) + +/// MCI data timeout configuration with 1048576 MCK cycles between 2 data transfers. +#define DTOR_1MEGA_CYCLES (AT91C_MCI_DTOCYC | AT91C_MCI_DTOMUL) + +/// MCI MR: disable MCI Clock when FIFO is full +#ifndef AT91C_MCI_WRPROOF + #define AT91C_MCI_WRPROOF 0 +#endif +#ifndef AT91C_MCI_RDPROOF + #define AT91C_MCI_RDPROOF 0 +#endif + +#define SDCARD_APP_OP_COND_CMD (41 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO ) +#define MMC_SEND_OP_COND_CMD (1 | AT91C_MCI_TRCMD_NO | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_OPDCMD) + + +#define DISABLE 0 // Disable MCI interface +#define ENABLE 1 // Enable MCI interface + + +//------------------------------------------------------------------------------ +// Local macros +//------------------------------------------------------------------------------ + +/// Used to write in PMC registers. +#define WRITE_PMC(pPmc, regName, value) pPmc->regName = (value) + +/// Used to write in MCI registers. +#define WRITE_MCI(pMci, regName, value) pMci->regName = (value) + +/// Used to read from MCI registers. +#define READ_MCI(pMci, regName) (pMci->regName) + +/// Enable MCI Peripheral +#define PERIPH_ENABLE(mciId) WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << mciId)) +/// Disable MCI Peripheral +#define PERIPH_DISABLE(mciId) WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << mciId)) + +/// Enable MCI +#define MCI_ENABLE(pMciHw) WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIEN) +/// Disable MCI +#define MCI_DISABLE(pMciHw) WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS) +/// Reset MCI +#define MCI_RESET(pMciHw) WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST) + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Delay some loop +//------------------------------------------------------------------------------ +static void Delay(volatile unsigned int loop) +{ + for(;loop > 0; loop --); +} + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Reset MCI interface and disable it. +/// \param keepSettings Keep old register settings, including _MR, _SDCR, _DTOR +//------------------------------------------------------------------------------ +void MCI_Reset(AT91PS_MCI pMciHw, unsigned int keepSettings) +{ + if (keepSettings) { + + unsigned int mciMr, mciSdcr, mciDtor; + + mciMr = READ_MCI(pMciHw, MCI_MR); + mciSdcr = READ_MCI(pMciHw, MCI_SDCR); + mciDtor = READ_MCI(pMciHw, MCI_DTOR); + + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST); + + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS); + WRITE_MCI(pMciHw, MCI_MR, mciMr); + WRITE_MCI(pMciHw, MCI_SDCR, mciSdcr); + WRITE_MCI(pMciHw, MCI_DTOR, mciDtor); + } + else { + + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST); + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS); + } +} + +//------------------------------------------------------------------------------ +/// Enable/disable a MCI driver instance. +/// \param pMci Pointer to a MCI driver instance. +/// \param enb 0 for disable MCI and 1 for enable MCI. +//------------------------------------------------------------------------------ +void MCI_Enable(Mci *pMci, unsigned char enb) +{ + AT91S_MCI *pMciHw = pMci->pMciHw; + + SANITY_CHECK(pMci); + SANITY_CHECK(pMci->pMciHw); + + // Set the Control Register: Enable/Disable MCI interface clock + if(enb == DISABLE) { + MCI_DISABLE(pMciHw); + } + else { + MCI_ENABLE(pMciHw); + } +} + +//------------------------------------------------------------------------------ +/// Initializes a MCI driver instance and the underlying peripheral. +/// \param pMci Pointer to a MCI driver instance. +/// \param pMciHw Pointer to a MCI peripheral. +/// \param mciId MCI peripheral identifier. +/// \param mode Slot and type of connected card. +//------------------------------------------------------------------------------ +void MCI_Init( + Mci *pMci, + AT91S_MCI *pMciHw, + unsigned char mciId, + unsigned int mode, + unsigned int bPolling) +{ + unsigned short clkDiv; + + SANITY_CHECK(pMci); + SANITY_CHECK(pMciHw); + SANITY_CHECK((mode == MCI_MMC_SLOTA) || (mode == MCI_SD_SLOTA) + || (mode == MCI_MMC_SLOTB) || (mode == MCI_SD_SLOTB)); + + // Initialize the MCI driver structure + pMci->pMciHw = pMciHw; + pMci->mciId = mciId; + pMci->mciMode = mode; + pMci->bPolling = bPolling; + pMci->semaphore = 1; + pMci->pCommand = 0; + +#if !defined(OP_BOOTSTRAP_MCI_on) || defined(at91sam9g20) + // Enable the MCI peripheral + PERIPH_ENABLE(mciId); + + // Reset the MCI + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST); + + // Disable the MCI + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS); + + // Disable all the interrupts + WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF); + + // Set the Data Timeout Register + WRITE_MCI(pMciHw, MCI_DTOR, DTOR_1MEGA_CYCLES); + + // Set the Mode Register: 400KHz for MCK = 48MHz (CLKDIV = 58) + clkDiv = (BOARD_MCK / (MCI_INITIAL_SPEED * 2)) - 1; + WRITE_MCI(pMciHw, MCI_MR, (clkDiv | (AT91C_MCI_PWSDIV & (0x7 << 8)))); + + // Set the SDCard Register + WRITE_MCI(pMciHw, MCI_SDCR, mode); + + // Enable the MCI and the Power Saving + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIEN); + + // Disable the MCI peripheral clock. + PERIPH_DISABLE(mciId); +#else + // Assume ROM code initialize the MCI already + TRACE_INFO("SD bootstrap not init mci!\n\r"); +#endif +} + +//------------------------------------------------------------------------------ +/// Close a MCI driver instance and the underlying peripheral. +/// \param pMci Pointer to a MCI driver instance. +/// \param pMciHw Pointer to a MCI peripheral. +/// \param mciId MCI peripheral identifier. +//------------------------------------------------------------------------------ +void MCI_Close(Mci *pMci) +{ + AT91S_MCI *pMciHw = pMci->pMciHw; + + SANITY_CHECK(pMci); + + if (!pMciHw) + return; + + // Disable the MCI + WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS); + + // Disable all the interrupts + WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF); + + // Disable the MCI peripheral clock. + WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pMci->mciId)); + + // Initialize the MCI driver structure + pMci->semaphore = 1; + pMci->pCommand = 0; +} + +//------------------------------------------------------------------------------ +/// Get the MCI CLKDIV in the MCI_MR register. The max. for MCI clock is +/// MCK/2 and corresponds to CLKDIV = 0 +/// \param pMci Pointer to the low level MCI driver. +/// \return The current speed used, 0 for fail. +//------------------------------------------------------------------------------ +unsigned int MCI_GetSpeed(Mci *pMci, unsigned int *mciDiv) +{ + AT91S_MCI *pMciHw = pMci->pMciHw; + unsigned int mciMr; + + SANITY_CHECK(pMci); + SANITY_CHECK(pMci->pMciHw); + + // Get the Mode Register + mciMr = READ_MCI(pMciHw, MCI_MR); + mciMr &= AT91C_MCI_CLKDIV; + if (mciDiv) *mciDiv = mciMr; + return (BOARD_MCK / 2 / (mciMr + 1)); +} + +//------------------------------------------------------------------------------ +/// Configure the MCI CLKDIV in the MCI_MR register. The max. for MCI clock is +/// MCK/2 and corresponds to CLKDIV = 0 +/// \param pMci Pointer to the low level MCI driver. +/// \param mciSpeed MCI clock speed in Hz. +/// \param mciLimit MCI clock limit in Hz, if not limit, set mciLimit to zero. +/// \param mck MCK to generate MCI Clock, in Hz +/// \return The actual speed used, 0 for fail. +//------------------------------------------------------------------------------ +unsigned int MCI_SetSpeed(Mci *pMci, + unsigned int mciSpeed, + unsigned int mciLimit, + unsigned int mck) +{ + AT91S_MCI *pMciHw = pMci->pMciHw; + unsigned int mciMr; + unsigned int clkdiv; + unsigned int divLimit = 0; + + SANITY_CHECK(pMci); + SANITY_CHECK(pMci->pMciHw); + + mciMr = READ_MCI(pMciHw, MCI_MR) & (~(unsigned int)AT91C_MCI_CLKDIV); + + // Multimedia Card Interface clock (MCCK or MCI_CK) is Master Clock (MCK) + // divided by (2*(CLKDIV+1)) + // mciSpeed = MCK / (2*(CLKDIV+1)) + if (mciLimit) { + divLimit = (mck / 2 / mciLimit); + if ((mck / 2) % mciLimit) divLimit ++; + } + if (mciSpeed > 0) { + clkdiv = (mck / 2 / mciSpeed); + if (mciLimit && clkdiv < divLimit) + clkdiv = divLimit; + if (clkdiv > 0) + clkdiv -= 1; + ASSERT( (clkdiv & 0xFFFFFF00) == 0, "mciSpeed too small"); + } + else clkdiv = 0; + + // Actual MCI speed + mciSpeed = mck / 2 / (clkdiv + 1); + + // Set the Data Timeout Register & Completion Timeout + // Data timeout is 500ms, completion timeout 1s. + //MCI_SetTimeout(pMciHw, mciSpeed / 2, mciSpeed); + + WRITE_MCI(pMciHw, MCI_MR, mciMr | clkdiv); + return (mciSpeed); +} + +//------------------------------------------------------------------------------ +/// Configure the MCI_CFG to enable the HS mode (MCI2 only) +/// \param pMci Pointer to the low level MCI driver. +/// \param hsEnable 1 to enable, 0 to disable HS mode. +//------------------------------------------------------------------------------ +void MCI_EnableHsMode(Mci *pMci, unsigned char hsEnable) +{ +} + +//------------------------------------------------------------------------------ +/// Configure the MCI SDCBUS in the MCI_SDCR register. Only two modes available +/// +/// \param pMci Pointer to the low level MCI driver. +/// \param busWidth MCI bus width mode. +//------------------------------------------------------------------------------ +void MCI_SetBusWidth(Mci *pMci, unsigned char busWidth) +{ + AT91S_MCI *pMciHw = pMci->pMciHw; + unsigned int mciSdcr; + + SANITY_CHECK(pMci); + SANITY_CHECK(pMci->pMciHw); + + mciSdcr = (READ_MCI(pMciHw, MCI_SDCR) & ~((unsigned int)AT91C_MCI_SCDBUS)); + + WRITE_MCI(pMciHw, MCI_SDCR, mciSdcr | busWidth); +} + +//------------------------------------------------------------------------------ +/// Starts a MCI transfer. This is a non blocking function. It will return +/// as soon as the transfer is started. +/// Return 0 if successful; otherwise returns MCI_ERROR_LOCK if the driver is +/// already in use. +/// \param pMci Pointer to an MCI driver instance. +/// \param pCommand Pointer to the command to execute. +//------------------------------------------------------------------------------ +unsigned char MCI_SendCommand(Mci *pMci, MciCmd *pCommand) +{ + AT91PS_MCI pMciHw = pMci->pMciHw; + unsigned int mciIer, mciMr; + + SANITY_CHECK(pMci); + SANITY_CHECK(pMciHw); + SANITY_CHECK(pCommand); + + // Try to acquire the MCI semaphore + if (pMci->semaphore == 0) { + + return MCI_ERROR_LOCK; + } + pMci->semaphore--; + // TRACE_DEBUG("MCI_SendCommand %x %d\n\r", READ_MCI(pMciHw, MCI_SR), pCommand->cmd & 0x3f); + + // Command is now being executed + pMci->pCommand = pCommand; + pCommand->status = MCI_STATUS_PENDING; + + // Enable the MCI Peripheral clock + WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pMci->mciId)); + + // Disable MCI clock + //MCI_DISABLE(pMciHw); + + // Init PDC direction or (SR status bits error occure) + if (pCommand->isRead) + WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN); + else + WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN); + + // Disable transmitter and receiver + WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + mciMr = READ_MCI(pMciHw, MCI_MR) & (~( AT91C_MCI_WRPROOF + | AT91C_MCI_RDPROOF + | AT91C_MCI_BLKLEN + | AT91C_MCI_PDCMODE)); + + // Command with DATA stage + if (pCommand->blockSize && pCommand->nbBlock) { + + #if defined(AT91C_MCI_PDCFBYTE) + // Force byte mode is block size is not 4n + if ((pCommand->blockSize & 0x3)) { + mciMr |= AT91C_MCI_PDCFBYTE; + + // Set byte count + WRITE_MCI(pMciHw, MCI_BLKR, (READ_MCI(pMciHw, MCI_BLKR) + & (~(unsigned int)AT91C_MCI_BCNT)) + | pCommand->nbBlock); + } + #endif + + // Enable PDC mode and set block size + if(pCommand->tranType != MCI_CONTINUE_TRANSFER) { + + + WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_PDCMODE + | AT91C_MCI_RDPROOF + | AT91C_MCI_WRPROOF + |(pCommand->blockSize << 16)); + } + + // Sanity check + if (pCommand->nbBlock == 0) + pCommand->nbBlock = 1; + + // DATA transfer from card to host + if (pCommand->isRead) { + WRITE_MCI(pMciHw, MCI_RPR, (int) pCommand->pData); + + // Update the PDC counter + #if defined(AT91C_MCI_PDCFBYTE) + if ((pCommand->blockSize & 0x3) != 0) { + // Force byte read + WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize)); + } + else + #endif + { + WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize) / 4); + } + WRITE_MCI(pMciHw, MCI_RNCR, 0); + + WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN); + mciIer = AT91C_MCI_ENDRX | STATUS_ERRORS; + } + + // DATA transfer from host to card + else { + + WRITE_MCI(pMciHw, MCI_TPR, (int) pCommand->pData); + // Update the PDC counter + #if defined(AT91C_MCI_PDCFBYTE) + if ((pCommand->blockSize & 0x3) != 0) { + // Force byte write + WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize)); + } + else + #endif + { + WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize) / 4); + } + WRITE_MCI(pMciHw, MCI_TNCR, 0); + // MCI_BLKE notifies the end of Multiblock command + mciIer = AT91C_MCI_ENDTX | STATUS_ERRORS; + } + } + // Start an infinite block transfer (but no data in current command) + else if (pCommand->dataTran) { + // Set block size + WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_RDPROOF + | AT91C_MCI_WRPROOF + |(pCommand->blockSize << 16)); + // Set data length: 0, no PDC operation + mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS; + } + // No data transfer: stop at the end of the command + else { + WRITE_MCI(pMciHw, MCI_MR, mciMr); + mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS; + } + // Enable MCI clock + MCI_ENABLE(pMciHw); + + // Send the command + if((pCommand->tranType != MCI_CONTINUE_TRANSFER) + || (pCommand->blockSize == 0)) { + + WRITE_MCI(pMciHw, MCI_ARGR, pCommand->arg); + WRITE_MCI(pMciHw, MCI_CMDR, pCommand->cmd); + } + + // In case of transmit, the PDC shall be enabled after sending the command + if (pCommand->blockSize > 0) { + if (!pCommand->isRead) { + WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN); + } + } + + // Ignore CRC error for R3 & R4 + if (pCommand->resType == 3 || pCommand->resType == 4) { + mciIer &= ~((unsigned int)AT91C_MCI_RCRCE); + } + // Ignore data error on stop command :) + if (pCommand->tranType == MCI_STOP_TRANSFER) { + mciIer &= ~(unsigned int)(AT91C_MCI_DTOE | AT91C_MCI_DCRCE); + } + + // Ignore data error + mciIer &= ~(unsigned int)(AT91C_MCI_UNRE | AT91C_MCI_OVRE + | AT91C_MCI_DTOE | AT91C_MCI_DCRCE); + + // Interrupt enable shall be done after PDC TXTEN and RXTEN + WRITE_MCI(pMciHw, MCI_IER, mciIer); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Check NOTBUSY and DTIP bits of status register on the given MCI driver. +/// Return value, 0 for bus ready, 1 for bus busy +/// \param pMci Pointer to a MCI driver instance. +//------------------------------------------------------------------------------ +unsigned char MCI_CheckBusy(Mci *pMci) +{ + AT91S_MCI *pMciHw = pMci->pMciHw; + unsigned int status; + + // Enable MCI clock + PERIPH_ENABLE(pMci->mciId); + MCI_ENABLE(pMciHw); + + status = READ_MCI(pMciHw, MCI_SR); + // TRACE_DEBUG("status %x\n\r",status); + + if(((status & AT91C_MCI_NOTBUSY)!=0) + && ((status & AT91C_MCI_DTIP)==0)) { + + // Disable MCI clock + MCI_DISABLE(pMciHw); + PERIPH_DISABLE(pMci->mciId); + + return 0; + } + else { + return 1; + } +} + +//------------------------------------------------------------------------------ +/// Processes pending events on the given MCI driver. +/// \param pMci Pointer to a MCI driver instance. +//------------------------------------------------------------------------------ +void MCI_Handler(Mci *pMci) +{ + AT91S_MCI *pMciHw = pMci->pMciHw; + MciCmd *pCommand = pMci->pCommand; + unsigned int status, status0, mask; + unsigned char i; + + SANITY_CHECK(pMci); + SANITY_CHECK(pMciHw); + SANITY_CHECK(pCommand); + + // Read the status register + status0 = READ_MCI(pMciHw, MCI_SR); + mask = READ_MCI(pMciHw, MCI_IMR); + status = status0 & mask; + TRACE_DEBUG("status %x & %x, %x\n\r", status0, mask, status); + + // Check if an error has occured + if ((status & STATUS_ERRORS) != 0) { + + // Check error code + if ((status & STATUS_ERRORS) == AT91C_MCI_RTOE) { + + pCommand->status = MCI_STATUS_NORESPONSE; + } + // if the command is SEND_OP_COND the CRC error flag is always present + // (cf : R3 response) + else if (((status & STATUS_ERRORS) != AT91C_MCI_RCRCE) + || ((pCommand->cmd != SDCARD_APP_OP_COND_CMD) + && (pCommand->cmd != MMC_SEND_OP_COND_CMD))) { + + pCommand->status = MCI_STATUS_ERROR; + } + } + + // Check if writing end + if (((status & AT91C_MCI_ENDTX) != 0) + || ((status & AT91C_MCI_TXBUFE) != 0)) { + WRITE_MCI(pMciHw, MCI_IDR, (AT91C_MCI_ENDTX|AT91C_MCI_TXBUFE)); + if ((status0 & AT91C_MCI_BLKE) == 0) + WRITE_MCI(pMciHw, MCI_IER, AT91C_MCI_BLKE); + else + status |= AT91C_MCI_BLKE; + } + + // Check if a transfer has been completed + if (((status & AT91C_MCI_CMDRDY) != 0) + || ((status & AT91C_MCI_ENDRX) != 0) + || ((status & AT91C_MCI_RXBUFF) != 0) + || ((status & AT91C_MCI_BLKE) != 0) + || ((status & AT91C_MCI_RTOE) != 0)) { + + // Store the card response in the provided buffer + if (pCommand->pResp) { + unsigned char resSize; + + switch (pCommand->resType) { + case 2: + resSize = 4; + break; + + case 1: case 3: case 4: case 5: case 6: case 7: + resSize = 1; + break; + + default: + resSize = 0; + break; + } + for (i=0; i < resSize; i++) { + + pCommand->pResp[i] = READ_MCI(pMciHw, MCI_RSPR[0]); + } + } + + TRACE_DEBUG(" ** CmdE\n\r"); + Delay(1000); + + // If no RD proof, FIFO may have dummy bytes + // Reset the MCI to clear these dummy data + #if (AT91C_MCI_RDPROOF == 0) + if ((pCommand->cmd & AT91C_MCI_TRCMD_STOP) != 0) { + MCI_Reset(pMciHw, 1); + } + #endif + + // Disable MCI + MCI_DISABLE(pMciHw); + + // Stop PDC + WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); + + // If no error occured, the transfer is successful + if (pCommand->status == MCI_STATUS_PENDING) { + pCommand->status = 0; + } + // If data in progress, reset MCI + else if (pCommand->dataTran) { + WRITE_MCI(pMciHw, MCI_TCR, 0); + WRITE_MCI(pMciHw, MCI_RCR, 0); + // Clear BUSY ... How ? + MCI_Reset(pMciHw, 1); + } +#if 0 + if ((status & AT91C_MCI_CMDRDY) != 0) + TRACE_DEBUG_WP("."); + if ((status & AT91C_MCI_ENDRX) != 0) + TRACE_DEBUG_WP("<"); + if ((status & AT91C_MCI_ENDTX) != 0) + TRACE_DEBUG_WP("-"); + if ((status & AT91C_MCI_BLKE) != 0) + TRACE_DEBUG_WP(">"); + TRACE_DEBUG_WP("\n\r"); +#endif + + // Disable interrupts + WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF); + + // Disable peripheral + PERIPH_DISABLE(pMci->mciId); + + // Release the semaphore + pMci->semaphore++; + + // Invoke the callback associated with the current command (if any) + if (pCommand->callback) { + (pCommand->callback)(pCommand->status, pCommand); + } + } +} + +//------------------------------------------------------------------------------ +/// Returns 1 if the given MCI transfer is complete; otherwise returns 0. +/// \param pCommand Pointer to a MciCmd instance. +//------------------------------------------------------------------------------ +unsigned char MCI_IsTxComplete(Mci *pMci) +{ + MciCmd *pCommand = pMci->pCommand; + + if(pMci->bPolling == MCI_POLLING_MODE) { + MCI_Handler(pMci); + } + + if (pCommand->status != MCI_STATUS_PENDING) { + if (pCommand->status != 0) { + TRACE_DEBUG("MCI_IsTxComplete %d\n\r", pCommand->status); + } + return 1; + } + else { + return 0; + } +} -- cgit v1.2.3