/* ---------------------------------------------------------------------------- * ATMEL Microcontroller Software Support * ---------------------------------------------------------------------------- * Copyright (c) 2009, 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. * ---------------------------------------------------------------------------- */ /** * \file * * Implementation of High Speed MultiMedia Card Interface (HSMCI) controller, * not using PDC nor DMA to transfer data. * */ /*--------------------------------------------------------------------------- * Headers *---------------------------------------------------------------------------*/ #include "chip.h" #include /*--------------------------------------------------------------------------- * Local macros *---------------------------------------------------------------------------*/ /** Reset MCI */ #define MCI_RESET(pMciHw) (pMciHw->HSMCI_CR = HSMCI_CR_SWRST) /*--------------------------------------------------------------------------- * Exported functions *---------------------------------------------------------------------------*/ /** * Enable MCI * \param pMciHw Pointer to a MCI peripheral. */ void MCI_Enable(Hsmci *pMciHw) { pMciHw->HSMCI_CR = HSMCI_CR_MCIEN; } /** * Disable MCI * \param pMciHw Pointer to a MCI peripheral. */ void MCI_Disable(Hsmci *pMciHw) { pMciHw->HSMCI_CR = HSMCI_CR_MCIDIS; } /** * 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. */ void MCI_Init( Mcid *pMci, Hsmci *pMciHw, uint8_t mciId, uint32_t dwMCk ) { unsigned short clkDiv; /* Initialize the MCI driver structure */ pMci->pMciHw = pMciHw; pMci->mciId = mciId; pMci->semaphore = 1; pMci->pCommand = NULL; /* Enable the MCI peripheral */ PMC_EnablePeripheral( mciId ) ; /* Reset the MCI */ pMciHw->HSMCI_CR = HSMCI_CR_SWRST; /* Disable the MCI */ pMciHw->HSMCI_CR = HSMCI_CR_MCIDIS | HSMCI_CR_PWSDIS; /* Disable all the interrupts */ pMciHw->HSMCI_IDR = 0xFFFFFFFF; /* Set the Data Timeout Register */ pMciHw->HSMCI_DTOR = HSMCI_DTOR_DTOCYC_Msk | HSMCI_DTOR_DTOMUL_Msk ; /* CSTOR ? */ pMciHw->HSMCI_CSTOR = HSMCI_CSTOR_CSTOCYC_Msk | HSMCI_CSTOR_CSTOMUL_Msk ; /* Set the Mode Register: 400KHz for MCK = 48MHz (CLKDIV = 58) */ clkDiv = (dwMCk / (MCI_INITIAL_SPEED * 2)) - 1; pMciHw->HSMCI_MR = (clkDiv | (HSMCI_MR_PWSDIV( 0x07 )) ) ; /* Set the SDCard Register 1-bit, slot A */ pMciHw->HSMCI_SDCR = HSMCI_SDCR_SDCSEL_SLOTA | HSMCI_SDCR_SDCBUS_1 ; /* Enable the MCI and the Power Saving */ pMciHw->HSMCI_CR = HSMCI_CR_MCIEN; /* Configure MCI */ pMciHw->HSMCI_CFG = HSMCI_CFG_FIFOMODE | ((1 << 4) & HSMCI_CFG_FERRCTRL); /* Disable the MCI peripheral clock. */ PMC_DisablePeripheral(mciId); } /** * 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, 0 will not change current speed. * \param mck MCK to generate MCI Clock, in Hz * \return The actual speed used, 0 for fail. */ uint32_t MCI_SetSpeed( Mcid* pMci, uint32_t mciSpeed, uint32_t mck ) { Hsmci *pMciHw = pMci->pMciHw; uint32_t mciMr; uint32_t clkdiv; uint8_t mciDis; assert(pMci); assert(pMciHw); PMC_EnablePeripheral(pMci->mciId); mciDis = PMC_IsPeriphEnabled(pMci->mciId); mciMr = pMciHw->HSMCI_MR & (~(uint32_t)HSMCI_MR_CLKDIV_Msk); /* Multimedia Card Interface clock (MCCK or MCI_CK) is Master Clock (MCK) * divided by (2*(CLKDIV+1)) * mciSpeed = MCK / (2*(CLKDIV+1)) */ if (mciSpeed > 0) { clkdiv = (mck / 2 / mciSpeed); /* Speed should not bigger than expired one */ if (mciSpeed < mck/2/clkdiv) { clkdiv ++; } if ( clkdiv > 0 ) { clkdiv -= 1; } assert( (clkdiv & 0xFFFFFF00) == 0 ) ; /* "mciSpeed too small" */ } else { clkdiv = 0 ; } /* Actual MCI speed */ mciSpeed = mck / 2 / (clkdiv + 1); /* Modify MR */ pMciHw->HSMCI_MR = mciMr | clkdiv; if ( mciDis ) { PMC_DisablePeripheral( pMci->mciId ) ; } 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. */ uint8_t MCI_EnableHsMode(Mcid* pMci, uint8_t hsEnable) { Hsmci *pMciHw = pMci->pMciHw; uint32_t cfgr; uint8_t mciDis; uint8_t rc = 0; assert(pMci); assert(pMci->pMciHw); PMC_EnablePeripheral(pMci->mciId); mciDis = PMC_IsPeriphEnabled(pMci->mciId); cfgr = pMciHw->HSMCI_CFG; if (hsEnable == 1) { cfgr |= HSMCI_CFG_HSMODE; } else { if (hsEnable == 0) { cfgr &= ~(uint32_t)HSMCI_CFG_HSMODE; } else { rc = ((cfgr & HSMCI_CFG_HSMODE) != 0); } } pMciHw->HSMCI_CFG = cfgr; if (mciDis) { PMC_DisablePeripheral(pMci->mciId); } return rc; } /** * 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. 00: 1-bit, 10: 4-bit, 11: 8-bit. */ uint32_t MCI_SetBusWidth(Mcid*pMci, uint32_t busWidth) { Hsmci *pMciHw = pMci->pMciHw; uint32_t mciSdcr; uint8_t mciDis; assert(pMci); assert(pMci->pMciHw); if( (busWidth != HSMCI_SDCR_SDCBUS_1) && (busWidth != HSMCI_SDCR_SDCBUS_4) && (busWidth != HSMCI_SDCR_SDCBUS_8) ) { return (uint32_t)-1; } busWidth &= HSMCI_SDCR_SDCBUS_Msk ; PMC_EnablePeripheral(pMci->mciId); mciDis = PMC_IsPeriphEnabled(pMci->mciId); mciSdcr = (pMciHw->HSMCI_SDCR & ~(uint32_t)(HSMCI_SDCR_SDCBUS_Msk)); pMciHw->HSMCI_SDCR = mciSdcr | busWidth; if (mciDis) { PMC_DisablePeripheral(pMci->mciId); } return 0; }