/* ---------------------------------------------------------------------------- * 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_hs.h" #include #include #include #include //------------------------------------------------------------------------------ // Local constants //------------------------------------------------------------------------------ /// Bit mask for status register errors. #define STATUS_ERRORS ((unsigned int)(AT91C_MCI_UNRE \ | AT91C_MCI_OVRE \ | AT91C_MCI_BLKOVRE \ | AT91C_MCI_CSTOE \ | AT91C_MCI_DTOE \ | AT91C_MCI_DCRCE \ | AT91C_MCI_RTOE \ | AT91C_MCI_RENDE \ | AT91C_MCI_RCRCE \ | AT91C_MCI_RDIRE \ | AT91C_MCI_RINDE)) #define STATUS_ERRORS_RESP ((unsigned int)(AT91C_MCI_CSTOE \ | AT91C_MCI_RTOE \ | AT91C_MCI_RENDE \ | AT91C_MCI_RCRCE \ | AT91C_MCI_RDIRE \ | AT91C_MCI_RINDE)) #define STATUS_ERRORS_DATA ((unsigned int)(AT91C_MCI_UNRE \ | AT91C_MCI_OVRE \ | AT91C_MCI_BLKOVRE \ | AT91C_MCI_CSTOE \ | AT91C_MCI_DTOE \ | AT91C_MCI_DCRCE)) /// 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 SDIO_SEND_OP_COND_CMD (5 | 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) /// Return word count from byte count #define toHWCOUNT(byteCnt) (((byteCnt)&0x1) ? (((byteCnt)/2)+1) : ((byteCnt)/2)) #define toWCOUNT(byteCnt) (((byteCnt)&0x3) ? (((byteCnt)/4)+1) : ((byteCnt)/4)) //------------------------------------------------------------------------------ // Local variables //------------------------------------------------------------------------------ //static unsigned char perChunkSize = 1; //static unsigned char memChunkSize = 1; //------------------------------------------------------------------------------ // Internal Functions //------------------------------------------------------------------------------ #if defined(MCI_DMA_ENABLE) #define MCI_RD_FIFO_LIMIT 1 #define MCI_FIFO_SIZE (0x4000-0x400) // 15.5K static unsigned short xfredBlocks = 0xFFFF; static unsigned char dmaLastLliNdx = 0; #define DMA_XFR_SIZE (0xF00) // SAM3: 0xFFF, 9M10: 0xFFFF #define NUM_LLI (5) #define DMA_TOTAL (DMA_XFR_SIZE*NUM_LLI) static DmaLinkList LLI_MCI [NUM_LLI]; // Max: > 64K Bytes #define LAST_ROW 0x100 static void AT91F_Prepare_Multiple_Transfer(unsigned int Channel, unsigned int LLI_rownumber, unsigned int LLI_Last_Row, unsigned int From_add, unsigned int To_add, unsigned int Ctrla, unsigned int Ctrlb) { LLI_MCI[LLI_rownumber].sourceAddress = From_add; LLI_MCI[LLI_rownumber].destAddress = To_add; LLI_MCI[LLI_rownumber].controlA = Ctrla; LLI_MCI[LLI_rownumber].controlB = Ctrlb; if (LLI_Last_Row != LAST_ROW) LLI_MCI[LLI_rownumber].descriptor = (unsigned int)&LLI_MCI[LLI_rownumber + 1] + 0; else { dmaLastLliNdx = LLI_rownumber; LLI_MCI[LLI_rownumber].descriptor = 0; } } // trans_size: number of transfers in SRC side // forceByte: 1 - byte count, 0 - word count static unsigned int DMACH_MCI_P2M(unsigned int channel_index, unsigned char* src_addr, unsigned char* dest_addr, unsigned int trans_size, unsigned char addrIncMode, unsigned char forceByte) { unsigned int srcAddress; unsigned int destAddress; unsigned int buffSize; unsigned int LLI_rownumber = 0; unsigned int srcAddressMode = addrIncMode ? (AT91C_HDMA_SRC_ADDRESS_MODE_INCR) : (AT91C_HDMA_SRC_ADDRESS_MODE_FIXED); unsigned int scSize, dcSize, mWidth, perWidth, addrInc; // Disable dma channel DMA_DisableChannel(channel_index); // DMA channel configuration srcAddress = (unsigned int)src_addr; // Set the data start address destAddress = (unsigned int)dest_addr; //(unsigned int)SSC_THR_ADD; buffSize = trans_size; // Memory width can be WORD if address is aligned mWidth = ((destAddress & 0x3) == 0) ? AT91C_HDMA_DST_WIDTH_WORD : AT91C_HDMA_DST_WIDTH_BYTE; // Peripheral width is byte if FBYTE mode perWidth = forceByte ? AT91C_HDMA_SRC_WIDTH_BYTE : AT91C_HDMA_SRC_WIDTH_WORD; addrInc = forceByte ? 1 : 4; #if MCI_RD_FIFO_LIMIT if(buffSize >= DMA_TOTAL){ TRACE_WARNING("SD DMA, size too big %d\n\r", buffSize); buffSize = DMA_TOTAL; } #endif // Set DMA channel source address DMA_SetSourceAddr(channel_index, srcAddress); // Set DMA channel destination address DMA_SetDestinationAddr(channel_index,destAddress); // Set DMA channel DSCR DMA_SetDescriptorAddr(channel_index, (unsigned int)&LLI_MCI[0]); // Set DMA channel control A DMA_SetSourceBufferSize(channel_index, buffSize, (perWidth >> 24), (mWidth >> 28), 0); //Set DMA channel control B DMA_SetSourceBufferMode(channel_index, DMA_TRANSFER_LLI, srcAddressMode >> 24); DMA_SetDestBufferMode(channel_index, DMA_TRANSFER_LLI, (AT91C_HDMA_DST_ADDRESS_MODE_INCR >> 28)); // Set DMA channel config DMA_SetConfiguration(channel_index, BOARD_SD_DMA_HW_SRC_REQ_ID \ | BOARD_SD_DMA_HW_DEST_REQ_ID \ | AT91C_HDMA_SRC_H2SEL_HW \ | AT91C_HDMA_DST_H2SEL_HW \ | AT91C_HDMA_SOD_DISABLE \ | AT91C_HDMA_FIFOCFG_ENOUGHSPACE); //scSize = (perChunkSize == 4) ? AT91C_HDMA_SCSIZE_4 : AT91C_HDMA_SCSIZE_1; //dcSize = (memChunkSize == 4) ? AT91C_HDMA_DCSIZE_4 : AT91C_HDMA_DCSIZE_1; scSize = AT91C_HDMA_SCSIZE_1; dcSize = AT91C_HDMA_DCSIZE_4; // Set link list buffSize *= addrInc; // convert size to byte count while(destAddress < ((unsigned int)(dest_addr + buffSize))) { if(((unsigned int)(dest_addr + buffSize)) - destAddress <= (DMA_XFR_SIZE*addrInc) ) { AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, LAST_ROW, srcAddress, destAddress, (((((unsigned int)(dest_addr + buffSize)) - destAddress)/addrInc) | perWidth | mWidth | scSize | dcSize ), ( AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM | AT91C_HDMA_DST_ADDRESS_MODE_INCR | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | srcAddressMode | AT91C_HDMA_FC_PER2MEM)); } else { AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, 0, srcAddress, destAddress, ( (DMA_XFR_SIZE) | perWidth | mWidth | scSize | dcSize ), ( AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM | AT91C_HDMA_DST_ADDRESS_MODE_INCR | AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | srcAddressMode | AT91C_HDMA_FC_PER2MEM)); } destAddress += DMA_XFR_SIZE*addrInc; LLI_rownumber++; } return 0; } // trans_size: number of transfers in SRC side // forceByte: 1 - byte count, 0 - word count static unsigned int DMACH_MCI_M2P(unsigned int channel_index, unsigned char* src_addr, unsigned char* dest_addr, unsigned int trans_size, unsigned char addrIncMode, unsigned char forceByte) { unsigned int srcAddress; unsigned int destAddress; unsigned int buffSize; unsigned int LLI_rownumber = 0; unsigned int dstAddressMode = addrIncMode ? (AT91C_HDMA_DST_ADDRESS_MODE_INCR) : (AT91C_HDMA_DST_ADDRESS_MODE_FIXED); unsigned int dcSize, scSize, mWidth, perWidth, addrInc; // Disable dma channel DMA_DisableChannel(channel_index); buffSize = trans_size; if(buffSize >= DMA_TOTAL){ TRACE_WARNING("SD DMA, size too big %d\n\r", buffSize); buffSize = DMA_TOTAL; } // DMA channel configuration srcAddress = (unsigned int)src_addr; // Set the data start address destAddress = (unsigned int)dest_addr; // Memory width mWidth = ((srcAddress & 0x3) == 0) ? AT91C_HDMA_SRC_WIDTH_WORD : AT91C_HDMA_SRC_WIDTH_BYTE; // One Transfer size (1 or 4) perWidth = forceByte ? AT91C_HDMA_DST_WIDTH_BYTE : AT91C_HDMA_DST_WIDTH_WORD; //addrInc = forceByte ? 1 : 4; if (mWidth == AT91C_HDMA_SRC_WIDTH_BYTE) { addrInc = 1; if (!forceByte) buffSize *= 4; } else addrInc = 4; // Set DMA channel source address DMA_SetSourceAddr(channel_index, srcAddress); // Set DMA channel destination address DMA_SetDestinationAddr(channel_index,destAddress); // Set DMA channel DSCR DMA_SetDescriptorAddr(channel_index, (unsigned int)&LLI_MCI[0]); // Set DMA channel control A DMA_SetSourceBufferSize(channel_index, buffSize, (mWidth >> 24), (perWidth >> 28), 0); //Set DMA channel control B DMA_SetSourceBufferMode(channel_index, DMA_TRANSFER_LLI, (AT91C_HDMA_SRC_ADDRESS_MODE_INCR >> 24)); DMA_SetDestBufferMode(channel_index, DMA_TRANSFER_LLI, dstAddressMode >> 28); // Set DMA channel config DMA_SetConfiguration(channel_index, BOARD_SD_DMA_HW_SRC_REQ_ID \ | BOARD_SD_DMA_HW_DEST_REQ_ID \ | AT91C_HDMA_SRC_H2SEL_SW \ | AT91C_HDMA_DST_H2SEL_HW \ | AT91C_HDMA_SOD_DISABLE \ | AT91C_HDMA_FIFOCFG_LARGESTBURST); //dcSize = (perChunkSize == 4) ? AT91C_HDMA_DCSIZE_4 : AT91C_HDMA_DCSIZE_1; //scSize = (memChunkSize == 4) ? AT91C_HDMA_SCSIZE_4 : AT91C_HDMA_SCSIZE_1; dcSize = AT91C_HDMA_DCSIZE_1; scSize = AT91C_HDMA_SCSIZE_4; // Set link list buffSize *= addrInc; // convert to byte address while(srcAddress < ((unsigned int)(src_addr + buffSize))) { if(((unsigned int)(src_addr + buffSize)) - srcAddress <= (DMA_XFR_SIZE*addrInc) ) { AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, LAST_ROW, srcAddress, destAddress, (((((unsigned int)(src_addr + buffSize)) - srcAddress)/addrInc) | mWidth | perWidth | scSize | dcSize ), ( AT91C_HDMA_DST_DSCR_FETCH_DISABLE | dstAddressMode | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM | AT91C_HDMA_SRC_ADDRESS_MODE_INCR | AT91C_HDMA_FC_MEM2PER)); } else { AT91F_Prepare_Multiple_Transfer(channel_index, LLI_rownumber, 0, srcAddress, destAddress, ( (DMA_XFR_SIZE) | mWidth | perWidth | scSize | dcSize ), ( AT91C_HDMA_DST_DSCR_FETCH_DISABLE | dstAddressMode | AT91C_HDMA_SRC_DSCR_FETCH_FROM_MEM | AT91C_HDMA_SRC_ADDRESS_MODE_INCR | AT91C_HDMA_FC_MEM2PER)); } srcAddress += DMA_XFR_SIZE*addrInc; LLI_rownumber++; } return 0; } static inline void DMACH_EnableIt(AT91S_MCI *pMciHw, unsigned int channel) { unsigned int intFlag; intFlag = DMA_GetInterruptMask(); intFlag |= (AT91C_HDMA_BTC0 << channel); DMA_EnableIt(intFlag); } #endif //------------------------------------------------------------------------------ // 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, mciCstor; unsigned int mciDma, mciCfg; mciMr = READ_MCI(pMciHw, MCI_MR); mciSdcr = READ_MCI(pMciHw, MCI_SDCR); mciDtor = READ_MCI(pMciHw, MCI_DTOR); mciCstor = READ_MCI(pMciHw, MCI_CSTOR); mciDma = READ_MCI(pMciHw, MCI_DMA); mciCfg = READ_MCI(pMciHw, MCI_CFG); 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); WRITE_MCI(pMciHw, MCI_CSTOR, mciCstor); WRITE_MCI(pMciHw, MCI_DMA, mciDma); WRITE_MCI(pMciHw, MCI_CFG, mciCfg); } 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 supported card (max bus width). //------------------------------------------------------------------------------ void MCI_Init( Mci *pMci, AT91PS_MCI pMciHw, unsigned char mciId, unsigned int mode, unsigned int bPolling) { unsigned short clkDiv; unsigned int mciCfg = 0; SANITY_CHECK(pMci); SANITY_CHECK(pMciHw); SANITY_CHECK( (mode == MCI_MMC_SLOTA) || (mode == MCI_SD_SLOTA) || (mode == MCI_MMC_SLOTB) || (mode == MCI_SD_SLOTB) || (mode == MCI_MMC4_SLOTA) || (mode == MCI_MMC4_SLOTB)); #if MCI_BUSY_CHECK_FIX pMci->pPinDAT0 = 0; #endif // 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) // Enable the MCI clock 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 DMA interface WRITE_MCI(pMciHw, MCI_DMA, AT91C_MCI_DMAEN_DISABLE); // Configure MCI mciCfg = AT91C_MCI_FIFOMODE_AMOUNTDATA | AT91C_MCI_FERRCTRL_RWCMD; //mciCfg = AT91C_MCI_FIFOMODE_ONEDATA | AT91C_MCI_FERRCTRL_RWCMD; WRITE_MCI(pMciHw, MCI_CFG, mciCfg); // Disable the MCI peripheral clock. WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << mciId)); #else // Assume ROM code initialize the MCI already TRACE_INFO("SD bootstrap not init mci!\n\r"); #endif } #if MCI_BUSY_CHECK_FIX /// Invoked after MCI_Init to enable busy line fix void MCI_SetBusyFix(Mci *pMci, const Pin * pDAT0) { pMci->pPinDAT0 = pDAT0; } #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 all the interrupts WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF); // Disable the MCI WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS); // 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. /// \param mciSpeed MCI clock speed in Hz. //------------------------------------------------------------------------------ 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 /// \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) { AT91S_MCI *pMciHw = pMci->pMciHw; unsigned int cfgr; SANITY_CHECK(pMci); SANITY_CHECK(pMci->pMciHw); cfgr = READ_MCI(pMciHw, MCI_CFG); if (hsEnable) cfgr |= AT91C_MCI_HSMODE_ENABLE; else cfgr &= ~(unsigned int)AT91C_MCI_HSMODE_ENABLE; WRITE_MCI(pMciHw, MCI_CFG, cfgr); } //------------------------------------------------------------------------------ /// 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; unsigned int transSize; unsigned int mciBlkr; #if defined(MCI_DMA_ENABLE) unsigned int mciDma; #endif 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--; // Command is now being executed pMci->pCommand = pCommand; pCommand->status = MCI_STATUS_PENDING; // Enable the MCI peripheral clock PERIPH_ENABLE(pMci->mciId); // Set Default Mode register value mciMr = READ_MCI(pMciHw, MCI_MR) & (~( AT91C_MCI_WRPROOF |AT91C_MCI_RDPROOF |AT91C_MCI_BLKLEN)); // Command with DATA stage if (pCommand->blockSize && pCommand->nbBlock) { // Enable dma #if defined(MCI_DMA_ENABLE) mciDma = READ_MCI(pMciHw, MCI_DMA) | AT91C_MCI_DMAEN_ENABLE; WRITE_MCI(pMciHw, MCI_DMA, mciDma); #endif transSize = (pCommand->nbBlock * pCommand->blockSize); #if defined(MCI_DMA_ENABLE) if (pCommand->isRead) { #if MCI_RD_FIFO_LIMIT if (transSize > MCI_FIFO_SIZE) { xfredBlocks = MCI_FIFO_SIZE/pCommand->blockSize; transSize = MCI_FIFO_SIZE; } else #endif { xfredBlocks = pCommand->nbBlock; } } else { xfredBlocks = pCommand->nbBlock; } #endif if ((pCommand->blockSize & 0x3) != 0) { // Force byte, DataReg & DMA should be BYTE based mciMr |= AT91C_MCI_PDCFBYTE; } else transSize = toWCOUNT(transSize); // New transfer if(pCommand->tranType == MCI_NEW_TRANSFER) { // Set block size WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_RDPROOF | AT91C_MCI_WRPROOF |(pCommand->blockSize << 16)); mciBlkr = READ_MCI(pMciHw, MCI_BLKR) & (~(unsigned int)AT91C_MCI_BCNT); WRITE_MCI(pMciHw, MCI_BLKR, mciBlkr | (transSize/pCommand->blockSize)); } // DATA transfer from card to host if (pCommand->isRead) { #if defined(MCI_DMA_ENABLE) DMACH_MCI_P2M(BOARD_MCI_DMA_CHANNEL, (unsigned char*)&pMciHw->MCI_FIFO, (unsigned char*) pCommand->pData, transSize, 0, (mciMr & AT91C_MCI_PDCFBYTE) > 0); DMACH_EnableIt(pMciHw, BOARD_MCI_DMA_CHANNEL); DMA_EnableChannel(BOARD_MCI_DMA_CHANNEL); mciIer = AT91C_MCI_DMADONE | STATUS_ERRORS; #else mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS; #endif } // DATA transfer from host to card else { #if defined(MCI_DMA_ENABLE) if ((mciMr & AT91C_MCI_PDCFBYTE) > 0) { #if 1 // Still using WORD mode to write FIFO DMACH_MCI_M2P(BOARD_MCI_DMA_CHANNEL, (unsigned char*) pCommand->pData, (unsigned char*)&pMciHw->MCI_FIFO, toWCOUNT(transSize), 0, 0); #else // Still write to TDR with WORD! DMACH_MCI_M2P(BOARD_MCI_DMA_CHANNEL, (unsigned char*) pCommand->pData, (unsigned char*)&pMciHw->MCI_TDR, toWCOUNT(transSize), 0, 0); #endif } else { DMACH_MCI_M2P(BOARD_MCI_DMA_CHANNEL, (unsigned char*) pCommand->pData, (unsigned char*)&pMciHw->MCI_FIFO, transSize, 0, 0); } DMACH_EnableIt(pMciHw, BOARD_MCI_DMA_CHANNEL); DMA_EnableChannel(BOARD_MCI_DMA_CHANNEL); mciIer = AT91C_MCI_DMADONE | STATUS_ERRORS; #else mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS; #endif } } // 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 mciBlkr = READ_MCI(pMciHw, MCI_BLKR) & (~(unsigned int)AT91C_MCI_BCNT); WRITE_MCI(pMciHw, MCI_BLKR, mciBlkr); 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 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); } // Ignore CRC error for R3 & R4 if (pCommand->resType == 3 || pCommand->resType == 4) { mciIer &= ~((unsigned int)AT91C_MCI_RCRCE); } // Ignore errors for stop command :) if (pCommand->tranType == MCI_STOP_TRANSFER) { mciIer &= ~((unsigned int)(AT91C_MCI_DCRCE |AT91C_MCI_BLKOVRE |AT91C_MCI_DTOE |AT91C_MCI_CSTOE)); } // Ignore data error mciIer &= ~( 0 | AT91C_MCI_UNRE | AT91C_MCI_OVRE | AT91C_MCI_DTOE | AT91C_MCI_DCRCE | AT91C_MCI_BLKOVRE | AT91C_MCI_CSTOE ); // 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; volatile unsigned int status; // Enable MCI clock PERIPH_ENABLE(pMci->mciId); MCI_ENABLE(pMciHw); status = READ_MCI(pMciHw, MCI_SR); #if MCI_BUSY_CHECK_FIX if ((status & AT91C_MCI_DTIP) == 0) { unsigned char isBusy = 1; if (pMci->pPinDAT0) { Pin pinBsy; pinBsy.mask = pMci->pPinDAT0->mask; pinBsy.pio = pMci->pPinDAT0->pio; pinBsy.id = pMci->pPinDAT0->id; pinBsy.type = PIO_INPUT; pinBsy.attribute = PIO_PULLUP; PIO_Configure(&pinBsy, 1); isBusy = (PIO_Get(&pinBsy) == 0); PIO_Configure(pMci->pPinDAT0, 1); } else { isBusy = ((status & AT91C_MCI_NOTBUSY) == 0); } if (isBusy == 0) { MCI_DISABLE(pMciHw); PERIPH_DISABLE(pMci->mciId); } return isBusy; } return 1; #else 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; } #endif } //------------------------------------------------------------------------------ /// 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; volatile unsigned int status; unsigned int 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); //TRACE_INFO("iST %x\n\r", status); status = status0 & mask; //TRACE_INFO("iSM %x\n\r", 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; } else { pCommand->status = MCI_STATUS_ERROR; } TRACE_INFO("iErr%x\n\r", (status & STATUS_ERRORS)); } mask &= ~STATUS_ERRORS; // Check if a command has been completed if (status & AT91C_MCI_CMDRDY) { WRITE_MCI(pMciHw, MCI_IDR, AT91C_MCI_CMDRDY); if (pCommand->isRead == 0 && pCommand->tranType == MCI_STOP_TRANSFER) { if (status0 & AT91C_MCI_XFRDONE) { MCI_DISABLE(pMciHw); } else { WRITE_MCI(pMciHw, MCI_IER, AT91C_MCI_XFRDONE); } } else { mask &= ~(unsigned int)AT91C_MCI_CMDRDY; if (pCommand->dataTran == 0) { MCI_DISABLE(pMciHw); } } } // Check if transfer stopped if (status & AT91C_MCI_XFRDONE) { mask &= ~(unsigned int)AT91C_MCI_XFRDONE; MCI_DISABLE(pMciHw); } #if defined(MCI_DMA_ENABLE) // Check FIFOEMPTY if (status & AT91C_MCI_FIFOEMPTY) { WRITE_MCI(pMciHw, MCI_IDR, AT91C_MCI_FIFOEMPTY); if ( pCommand->isRead == 0 && (status0 & AT91C_MCI_BLKE) == 0 ) { WRITE_MCI(pMciHw, MCI_IER, AT91C_MCI_BLKE); } else { mask &= ~(unsigned int)AT91C_MCI_FIFOEMPTY; MCI_DISABLE(pMciHw); } } if (status & AT91C_MCI_BLKE) { mask &= ~(unsigned int)AT91C_MCI_BLKE; MCI_DISABLE(pMciHw); } // Check if a DMA transfer has been completed if ( (status & AT91C_MCI_DMADONE) && (LLI_MCI[dmaLastLliNdx].controlA & AT91C_HDMA_DONE)) { unsigned int intFlag; intFlag = DMA_GetInterruptMask(); intFlag = ~intFlag; intFlag |= (AT91C_HDMA_BTC0 << BOARD_MCI_DMA_CHANNEL); DMA_DisableIt(intFlag); // All data transferred if (xfredBlocks >= pCommand->nbBlock) { WRITE_MCI(pMciHw, MCI_IDR, AT91C_MCI_DMADONE); if ( pCommand->isRead == 0 && (status0 & AT91C_MCI_FIFOEMPTY) == 0 ) { WRITE_MCI(pMciHw, MCI_IER, AT91C_MCI_FIFOEMPTY); } else { MCI_DISABLE(pMciHw); mask &= ~(unsigned int)AT91C_MCI_DMADONE; } } // Start later part of DMA else { unsigned int transSize; unsigned char* p; p = &pCommand->pData[xfredBlocks*pCommand->blockSize]; transSize = ((pCommand->nbBlock - xfredBlocks) * pCommand->blockSize); if (transSize > MCI_FIFO_SIZE) { transSize = MCI_FIFO_SIZE; xfredBlocks += MCI_FIFO_SIZE/pCommand->blockSize; } else { xfredBlocks = pCommand->nbBlock; } #if 0 WRITE_MCI(pMciHw, MCI_BLKR, (READ_MCI(pMciHw, MCI_BLKR) & (~(unsigned int)AT91C_MCI_BCNT)) | (transSize/pCommand->blockSize)); #endif if ((pCommand->blockSize & 0x3) == 0) { transSize = toWCOUNT(transSize); } DMACH_MCI_P2M(BOARD_MCI_DMA_CHANNEL, (unsigned char*)&pMciHw->MCI_FIFO, (unsigned char*) p, transSize, 0, (pCommand->blockSize & 0x3) > 0); DMA_EnableChannel(BOARD_MCI_DMA_CHANNEL); } } #endif // All non-error mask done, complete the command if (!mask || pCommand->status != MCI_STATUS_PENDING) { // Store the card response in the provided buffer if (pCommand->pResp) { unsigned char resSize; switch (pCommand->resType) { case 1: case 3: case 4: case 5: case 6: case 7: resSize = 1; break; case 2: resSize = 4; break; default: resSize = 0; break; } for (i=0; i < resSize; i++) { pCommand->pResp[i] = READ_MCI(pMciHw, MCI_RSPR[0]); } } // If no error occured, the transfer is successful if (pCommand->status == MCI_STATUS_PENDING) pCommand->status = 0; // Any error, reset registers else { MCI_Reset(pMciHw, 1); } // Disable interrupts WRITE_MCI(pMciHw, MCI_IDR, READ_MCI(pMciHw, MCI_IMR)); #if defined(MCI_DMA_ENABLE) DMA_DisableChannel(BOARD_MCI_DMA_CHANNEL); #endif // 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, (void*)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; } } //------------------------------------------------------------------------------ /// Check whether the MCI is using the FIFO transfer mode /// \param pMci Pointer to a Mci instance. /// \param pCommand Pointer to a MciCmd instance. //------------------------------------------------------------------------------ unsigned int MCI_FifoTransfer(Mci *pMci, MciCmd *pCommand) { // If using DMA mode, return #if defined(MCI_DMA_ENABLE) return 0; #else unsigned int status=0; unsigned int nbTransfer=0; unsigned int i; AT91S_MCI *pMciHw = pMci->pMciHw; unsigned int *pMem; SANITY_CHECK(pMci); SANITY_CHECK(pCommand); TRACE_DEBUG("MCIFifo:%d,%d\n\r", pCommand->isRead, pCommand->nbBlock); if (pCommand->nbBlock == 0 || pCommand->blockSize == 0) return 0; pMem = (unsigned int*)pCommand->pData; // Get transfer size nbTransfer = (pCommand->blockSize) * (pCommand->nbBlock) / 4; if((pCommand->blockSize) * (pCommand->nbBlock) % 4) { nbTransfer++; } if (pCommand->isRead) { // Read RDR loop for(i=0; i %x", status); } } TRACE_DEBUG_WP("\n\r"); TRACE_DEBUG(" DPIT 0 stat %x\n\r", status); while((status & (AT91C_MCI_FIFOEMPTY | AT91C_MCI_BLKE | AT91C_MCI_XFRDONE)) == 0) { status = READ_MCI(pMciHw, MCI_SR); } TRACE_DEBUG(" FIFO EMPTY stat %x\n\r", status); } #endif return status; #endif }