| /* |
| * NDA AND NEED-TO-KNOW REQUIRED |
| * |
| * Copyright © 2013-2018 Synaptics Incorporated. All rights reserved. |
| * |
| * This file contains information that is proprietary to Synaptics |
| * Incorporated ("Synaptics"). The holder of this file shall treat all |
| * information contained herein as confidential, shall use the |
| * information only for its intended purpose, and shall not duplicate, |
| * disclose, or disseminate any of this information in any manner |
| * unless Synaptics has otherwise provided express, written |
| * permission. |
| * |
| * Use of the materials may require a license of intellectual property |
| * from a third party or from Synaptics. This file conveys no express |
| * or implied licenses to any intellectual property rights belonging |
| * to Synaptics. |
| * |
| * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND |
| * SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, |
| * INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY |
| * INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR |
| * CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE |
| * OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND |
| * BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF |
| * COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT |
| * DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY |
| * TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS. |
| */ |
| |
| /****************************************************************************** |
| ** |
| ** FILENAME: emmcHC.c |
| ** |
| ** PURPOSE: MMC and SD specific low level controller routines for the MM4 controller |
| ** |
| ** |
| ** |
| ** |
| ******************************************************************************/ |
| #include "com_type.h" |
| #include "emmcHC.h" |
| #include "debug.h" |
| #include "io.h" |
| |
| //#define EMMC_DEBUG |
| #ifdef EMMC_DEBUG |
| #define EMMC_PRN(PRN_LEVEL,fmt, args...) dbg_printf(PRN_LEVEL, fmt, ## args) |
| #else |
| #define EMMC_PRN(PRN_LEVEL,fmt, args...) |
| #endif |
| |
| /****************************************************************************** |
| Description: |
| Start MMC bus clock. Only after starting bus clock, communication between |
| controller and card is possible |
| Input Parameters: |
| pContext--Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| #if 0 |
| void MMC4ModeSelect(P_MM4_SDMMC_CONTEXT_T pContext, UINT_T sd_mode) |
| { |
| P_MM4_CNTL1 pMM4_CNTL1; |
| MM4_CNTL1_UNION MM4_cntl1; |
| |
| pMM4_CNTL1 = (P_MM4_CNTL1)((VUINT32_T) &pContext->pMMC4Reg->mm4_cntl1); |
| MM4_cntl1.mm4_cntl1_value = *(VUINT_T *)pMM4_CNTL1; |
| MM4_cntl1.mm4_cntl1_bits.eightbitmd = sd_mode; |
| |
| *(VUINT_T *)pMM4_CNTL1 = MM4_cntl1.mm4_cntl1_value; |
| return; |
| } |
| #endif |
| void EMMCHC_HighSpeedSelect(P_MM4_SDMMC_CONTEXT_T pContext, UINT_T hs_mode) |
| { |
| P_MM4_CNTL1 pMM4_CNTL1; |
| MM4_CNTL1_UNION MM4_cntl1; |
| |
| pMM4_CNTL1 = (P_MM4_CNTL1)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl1); |
| MM4_cntl1.mm4_cntl1_value = *(VUINT_T *)pMM4_CNTL1; |
| MM4_cntl1.mm4_cntl1_bits.hispeed = hs_mode & 0x1; |
| |
| *(VUINT_T *)pMM4_CNTL1 = MM4_cntl1.mm4_cntl1_value; |
| return; |
| } |
| |
| /******************************************************************* |
| * BG6CD only support HS-SDR and HS-DDR mode |
| * set emmc mode, mode0: high speed SDR, mode1:high speed DDR |
| ********************************************************************/ |
| void EMMCHC_host_mode(P_MM4_SDMMC_CONTEXT_T pContext, UINT_T uhs_mode) |
| { |
| volatile P_MM4_ACMD12_ER pMM4_crtl2; |
| UINT32_T MM4_crtl2; |
| |
| MM4_crtl2 = pContext->pMMC4Reg->mm4_acmd12_er; |
| pMM4_crtl2 = (volatile P_MM4_ACMD12_ER)&MM4_crtl2; |
| |
| if(uhs_mode == 0) |
| pMM4_crtl2->uhs_mode_sel = 1; //HS-SDR |
| else if(uhs_mode == 1) |
| pMM4_crtl2->uhs_mode_sel = 4; //HS DDR |
| else if(uhs_mode == 2) |
| pMM4_crtl2->uhs_mode_sel = 3; //HS200 |
| else if(uhs_mode == 3) |
| pMM4_crtl2->uhs_mode_sel = 7; //HS400 |
| |
| pContext->pMMC4Reg->mm4_acmd12_er = MM4_crtl2; |
| } |
| |
| #if 0 |
| void MMC4BusWidthSelect(P_MM4_SDMMC_CONTEXT_T pContext, UINT_T sd_4bit) |
| { |
| P_MM4_CNTL1 pMM4_CNTL1; |
| MM4_CNTL1_UNION MM4_cntl1; |
| |
| pMM4_CNTL1 = (P_MM4_CNTL1)((VUINT32_T) &pContext->pMMC4Reg->mm4_cntl1); |
| MM4_cntl1.mm4_cntl1_value = *(VUINT_T *)pMM4_CNTL1; |
| MM4_cntl1.mm4_cntl1_bits.datawidth = sd_4bit; |
| |
| *(VUINT_T *)pMM4_CNTL1 = MM4_cntl1.mm4_cntl1_value; |
| return; |
| } |
| #endif |
| |
| |
| |
| #if 1 |
| void EMMCHC_DmaSelect(P_MM4_SDMMC_CONTEXT_T pContext, UINT_T select) |
| { |
| P_MM4_CNTL1 pMM4_CNTL1; |
| MM4_CNTL1_UNION MM4_cntl1; |
| |
| pMM4_CNTL1 = (P_MM4_CNTL1)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl1); |
| MM4_cntl1.mm4_cntl1_value = *(VUINT_T *)pMM4_CNTL1; |
| MM4_cntl1.mm4_cntl1_bits.dma_sel = select & 0x3; |
| |
| *(VUINT_T *)pMM4_CNTL1 = MM4_cntl1.mm4_cntl1_value; |
| return; |
| } |
| #endif |
| /****************************************************************************** |
| Description: |
| Start MMC/SD Internal bus clock. MUST be done to start MM4CLK! |
| Only after starting bus clock, communication between |
| controller and card is possible |
| Input Parameters: |
| pContext--Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_StartInternalBusClock(P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.inter_clk_en = 1; |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| |
| // Wait for clock to become stable. * TBD * Add timeout |
| delay_ms(10); |
| do |
| { |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| delay_us(1); |
| } while (!MM4_cntl2.mm4_cntl2_bits.inter_clk_stable); |
| |
| return; |
| } |
| |
| /****************************************************************************** |
| Description: |
| Stops the MMC/SD Internal bus clock. |
| Input Parameters: |
| pContext--Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_StopInternalBusClock(P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.inter_clk_en = 0; |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| |
| // Wait for clock to become stable. * TBD * Add timeout |
| do |
| { |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| } while (MM4_cntl2.mm4_cntl2_bits.inter_clk_stable); |
| |
| return; |
| } |
| |
| |
| /****************************************************************************** |
| Description: |
| Start MMC bus clock. Only after starting bus clock, communication between |
| controller and card is possible |
| Input Parameters: |
| pContext--Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_StartBusClock(P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| |
| MM4_cntl2.mm4_cntl2_bits.mm4clken = 1; |
| |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| return; |
| } |
| |
| /****************************************************************************** |
| Description: |
| Stops MMC bus clock. |
| Input Parameters: |
| pContext--Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_StopBusClock (P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| //UINT32_T retry_count = 0xff; |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| // Request bus clock stop |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.mm4clken = 0; |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| |
| return; |
| } |
| |
| extern void EMMC_PhyInitialization(void); |
| |
| /****************************************************************************** |
| Description: |
| Set a new MMC bus clock rate. This function stops and resumes bus clock. |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| rate |
| bus clock speed |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_SetBusRate(P_MM4_SDMMC_CONTEXT_T pContext, UINT_T rate) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| unsigned int timeout = 0; |
| |
| EMMC_PRN(PRN_INFO,"Start to setup freq divide rate = %d\n", rate); |
| // Request bus clock stop, set rate, start clock. |
| //EMMCHC_StopBusClock(pContext); |
| |
| // BG6CD/NIUE design, internal clock must be off to change clock divider |
| // and on again to sync to target clock. |
| //EMMCHC_StopInternalBusClock(pContext); |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.mm4clken = 0; |
| MM4_cntl2.mm4_cntl2_bits.inter_clk_en = 0; |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| // Update the rate and start the clock. |
| MM4_cntl2.mm4_cntl2_bits.sd_freq_sel_lo = (rate & 0xFF); |
| MM4_cntl2.mm4_cntl2_bits.sd_freq_sel_hi = ((rate >> 8) & 3); |
| MM4_cntl2.mm4_cntl2_bits.inter_clk_en = 1; |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| |
| // Wait for clock to become stable. * TBD * Add timeout |
| /* max 20ms */ |
| timeout = 20; |
| do { |
| if(timeout == 0) { |
| dbg_printf(PRN_ERR, "%s: Internal clock never stabilised.\n", |
| __func__); |
| return; |
| } |
| |
| delay_ms(1); |
| timeout--; |
| |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| } while (!MM4_cntl2.mm4_cntl2_bits.inter_clk_stable); |
| //-------------------------------------------------- |
| //EMMCHC_StartInternalBusClock(pContext); |
| EMMC_PRN(PRN_INFO,"freq divide rate = %d\n", rate); |
| EMMC_PhyInitialization(); |
| |
| //---------------------------------------------- |
| EMMCHC_StartBusClock(pContext); |
| return; |
| } |
| |
| |
| /****************************************************************************** |
| Description: |
| This routine unmasks and enables or masks and disables required interrupts |
| needed by the driver |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| Desire - Enable or Disable the interrupts |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_EnableDisableIntSources(P_MM4_SDMMC_CONTEXT_T pContext, UINT8_T Desire) |
| { |
| P_MM4_I_STAT pMM4_I_STAT; |
| MM4_I_STAT_UNION MM4_i_stat; |
| |
| // Capture existing Value |
| pMM4_I_STAT = (P_MM4_I_STAT)((uintptr_t) &pContext->pMMC4Reg->mm4_i_sig_en); |
| |
| MM4_i_stat.mm4_i_stat_value = *(VUINT_T*)pMM4_I_STAT; |
| // Route the interrupt signal enable register |
| MM4_i_stat.mm4_i_stat_bits.cmdcomp = Desire; |
| MM4_i_stat.mm4_i_stat_bits.xfrcomp = Desire; |
| MM4_i_stat.mm4_i_stat_bits.bufwrrdy = Desire; |
| MM4_i_stat.mm4_i_stat_bits.bufrdrdy = Desire; |
| // KT added for card detection |
| MM4_i_stat.mm4_i_stat_bits.cdins = Desire; |
| MM4_i_stat.mm4_i_stat_bits.cdrem = Desire; |
| // |
| MM4_i_stat.mm4_i_stat_bits.cdint = Desire; |
| MM4_i_stat.mm4_i_stat_bits.errint = Desire; |
| MM4_i_stat.mm4_i_stat_bits.ctoerr = Desire; // KT added |
| MM4_i_stat.mm4_i_stat_bits.ccrcerr= Desire; // KT added |
| MM4_i_stat.mm4_i_stat_bits.cenderr = Desire; |
| MM4_i_stat.mm4_i_stat_bits.cidxerr = Desire; // |
| MM4_i_stat.mm4_i_stat_bits.dtoerr = Desire; |
| MM4_i_stat.mm4_i_stat_bits.dcrcerr = Desire; |
| MM4_i_stat.mm4_i_stat_bits.denderr = Desire; |
| #if SDIO_DMA |
| MM4_i_stat.mm4_i_stat_bits.dmaint = Desire; //KT added |
| MM4_i_stat.mm4_i_stat_bits.ac12err = Desire; //KT added auto CMD12 error |
| #endif |
| |
| // Write it out |
| *(VUINT_T*)pMM4_I_STAT = MM4_i_stat.mm4_i_stat_value; |
| |
| // Now remove the masks |
| pMM4_I_STAT = (P_MM4_I_STAT)((uintptr_t) &pContext->pMMC4Reg->mm4_i_stat_en); |
| MM4_i_stat.mm4_i_stat_value = *(VUINT_T*)pMM4_I_STAT; |
| MM4_i_stat.mm4_i_stat_bits.cmdcomp = Desire; |
| MM4_i_stat.mm4_i_stat_bits.xfrcomp = Desire; |
| MM4_i_stat.mm4_i_stat_bits.bufwrrdy = Desire; |
| MM4_i_stat.mm4_i_stat_bits.bufrdrdy = Desire; |
| // KT added for card detection |
| MM4_i_stat.mm4_i_stat_bits.cdins = Desire; |
| MM4_i_stat.mm4_i_stat_bits.cdrem = Desire; |
| // |
| #ifndef CONFIG_FPGA |
| MM4_i_stat.mm4_i_stat_bits.cdint = Desire;// --> Satya ; KT removed the workaround |
| #endif |
| MM4_i_stat.mm4_i_stat_bits.errint = Desire; |
| MM4_i_stat.mm4_i_stat_bits.ctoerr = Desire; |
| MM4_i_stat.mm4_i_stat_bits.ccrcerr= Desire; // KT added |
| MM4_i_stat.mm4_i_stat_bits.cenderr = Desire; // |
| MM4_i_stat.mm4_i_stat_bits.cidxerr = Desire; // |
| MM4_i_stat.mm4_i_stat_bits.dtoerr = Desire; |
| MM4_i_stat.mm4_i_stat_bits.dcrcerr = Desire; |
| MM4_i_stat.mm4_i_stat_bits.denderr = Desire; |
| |
| #if SDIO_DMA |
| MM4_i_stat.mm4_i_stat_bits.dmaint = Desire; //KT added |
| MM4_i_stat.mm4_i_stat_bits.ac12err = Desire; //KT added auto CMD12 error |
| #endif |
| |
| // Write it out |
| *(VUINT_T*)pMM4_I_STAT = MM4_i_stat.mm4_i_stat_value; |
| |
| EMMC_PRN(PRN_INFO, "\n MM4_i_stat.mm4_i_stat_value is 0x%x\n", MM4_i_stat.mm4_i_stat_value); |
| |
| |
| return; |
| } |
| |
| /****************************************************************************** |
| Description: |
| Set the data response timeout value. |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| CounterValue |
| the value which will be written into DTOCNTR |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_SetDataTimeout(P_MM4_SDMMC_CONTEXT_T pContext, UINT8_T CounterValue) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| // Set the register |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.dtocntr = CounterValue; |
| |
| // Write Back |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| return; |
| } |
| |
| /****************************************************************************** |
| Description: |
| This function will induce a software reset of all MMC4 data lines |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_DataSWReset(P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| // Set the register |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.datswrst = 1; |
| |
| // Write Back |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| return; |
| } |
| |
| /****************************************************************************** |
| Description: |
| This function will induce a full software reset of all MMC4 components except |
| MM4_CAPX |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_FullSWReset(P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| // Set the register |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.mswrst = 1; |
| |
| // Write Back |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| return; |
| } |
| |
| /****************************************************************************** |
| Description: |
| This function will induce a software reset of all MMC4 data lines |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_CMDSWReset(P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| P_MM4_CNTL2 pMM4_CNTL2; |
| MM4_CNTL2_UNION MM4_cntl2; |
| |
| // Set the register |
| pMM4_CNTL2 = (P_MM4_CNTL2)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl2); |
| MM4_cntl2.mm4_cntl2_value = *(VUINT_T *)pMM4_CNTL2; |
| MM4_cntl2.mm4_cntl2_bits.cmdswrst = 1; |
| |
| // Write Back |
| *(VUINT_T *)pMM4_CNTL2 = MM4_cntl2.mm4_cntl2_value; |
| return; |
| } |
| |
| |
| /**************************************************************************/ |
| void EMMCHC_StopAtBlockGap(P_MM4_SDMMC_CONTEXT_T pContext) |
| { |
| P_MM4_CNTL1 pMM4_CNTL1; |
| MM4_CNTL1_UNION MM4_cntl1; |
| |
| // Set the register |
| pMM4_CNTL1 = (P_MM4_CNTL1)((uintptr_t) &pContext->pMMC4Reg->mm4_cntl1); |
| MM4_cntl1.mm4_cntl1_value = *(VUINT_T *)pMM4_CNTL1; |
| MM4_cntl1.mm4_cntl1_bits.bgreqstp = 1; |
| |
| // Write Back |
| *(VUINT_T *)pMM4_CNTL1 = MM4_cntl1.mm4_cntl1_value; |
| return; |
| } |
| /****************************************************************************** |
| Description: |
| Set up the registers of the controller to start the transaction to |
| communicate to the card for data related command. The commands are clearly defined in the MMC |
| specification. |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| Cmd |
| Command Index - See MMC or SD specification |
| argument |
| the argument of the command. MSW is for ARGH and LSW is for ARGL |
| BlockType |
| Multiple or Single Block Type |
| ResType |
| Expected response type |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_SendDataCommand(P_MM4_SDMMC_CONTEXT_T pContext, |
| UINT_T Cmd, |
| UINT_T Argument, |
| UINT_T BlockType, |
| UINT_T DataDirection, |
| UINT_T ResType) |
| { |
| MM4_CMD_XFRMD_UNION xfrmd; |
| P_MM4_STATE pMM4_STATE; |
| |
| // Make sure the controller is ready to accept the next command |
| pMM4_STATE = (P_MM4_STATE) &pContext->pMMC4Reg->mm4_state; |
| while (pMM4_STATE->dcmdinhbt) |
| {;} // Wait. |
| |
| // Set the Argument Field |
| pContext->pMMC4Reg->mm4_arg = Argument; |
| #if 0 |
| // Set the Data Transfer Command fields. |
| xfrmd.mm4_cmd_xfrmd_value = 0; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_idx = Cmd; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_type = MM4_CMD_TYPE_NORMAL; |
| xfrmd.mm4_cmd_xfrmd_bits.dpsel = MM4_CMD_DATA; |
| xfrmd.mm4_cmd_xfrmd_bits.idxchken = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.crcchken = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.res_type = ResType; |
| xfrmd.mm4_cmd_xfrmd_bits.ms_blksel = BlockType; |
| xfrmd.mm4_cmd_xfrmd_bits.dxfrdir = DataDirection; |
| //xfrmd.mm4_cmd_xfrmd_bits.autocmd12 = TRUE; |
| //xfrmd.mm4_cmd_xfrmd_bits.blkcbten = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.autocmd12 = FALSE; |
| xfrmd.mm4_cmd_xfrmd_bits.blkcbten = FALSE; |
| #endif |
| |
| // Set the Data Transfer Command fields. |
| xfrmd.mm4_cmd_xfrmd_value = 0; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_idx = Cmd; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_type = MM4_CMD_TYPE_NORMAL; |
| xfrmd.mm4_cmd_xfrmd_bits.dpsel = MM4_CMD_DATA; |
| xfrmd.mm4_cmd_xfrmd_bits.idxchken = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.crcchken = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.res_type = ResType; |
| xfrmd.mm4_cmd_xfrmd_bits.ms_blksel = BlockType; |
| xfrmd.mm4_cmd_xfrmd_bits.dxfrdir = DataDirection; |
| xfrmd.mm4_cmd_xfrmd_bits.autocmd12 = FALSE; |
| xfrmd.mm4_cmd_xfrmd_bits.blkcbten = TRUE; |
| |
| #if SDIO_DMA |
| // enable DMA |
| xfrmd.mm4_cmd_xfrmd_bits.dma_en = 1; |
| #if 1 |
| if (Cmd==25 || Cmd==18 ) |
| { // multiple blocks with Auto STOP command |
| xfrmd.mm4_cmd_xfrmd_bits.autocmd12 = TRUE; |
| // xfrmd.mm4_cmd_xfrmd_bits.blkcbten = TRUE; |
| |
| // Stop at Block Gap request; set bit 0 of 2Ah |
| //MMC4StopAtBlockGap(pContext); |
| } |
| #endif |
| |
| #endif |
| EMMC_PRN(PRN_DBG,"\n Sending command: xfrmd = 0x%x ",xfrmd.mm4_cmd_xfrmd_value); |
| // Kick off the command |
| pContext->pMMC4Reg->mm4_cmd_xfrmd = xfrmd.mm4_cmd_xfrmd_value; |
| return; |
| } |
| |
| /****************************************************************************** |
| Description: |
| Set up the registers of the controller to start the transaction to |
| communicate to the card for data related command. The commands are clearly defined in the MMC |
| specification. |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| Cmd |
| Command Index - See MMC or SD specification |
| argument |
| the argument of the command. MSW is for ARGH and LSW is for ARGL |
| BlockType |
| Multiple or Single Block Type |
| ResType |
| Expected response type |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_SendDataCommandNoAuto12(P_MM4_SDMMC_CONTEXT_T pContext, |
| UINT_T Cmd, |
| UINT_T Argument, |
| UINT_T BlockType, |
| UINT_T DataDirection, |
| UINT_T ResType) |
| { |
| MM4_CMD_XFRMD_UNION xfrmd; |
| P_MM4_STATE pMM4_STATE; |
| |
| // Make sure the controller is ready to accept the next command |
| pMM4_STATE = (P_MM4_STATE) &pContext->pMMC4Reg->mm4_state; |
| while (pMM4_STATE->dcmdinhbt) |
| {;} // Wait. |
| |
| // Set the Argument Field |
| pContext->pMMC4Reg->mm4_arg = Argument; |
| |
| // Set the Data Transfer Command fields. |
| xfrmd.mm4_cmd_xfrmd_value = 0; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_idx = Cmd; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_type = MM4_CMD_TYPE_NORMAL; |
| xfrmd.mm4_cmd_xfrmd_bits.dpsel = MM4_CMD_DATA; |
| // xfrmd.mm4_cmd_xfrmd_bits.idxchken = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.crcchken = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.res_type = ResType; |
| xfrmd.mm4_cmd_xfrmd_bits.ms_blksel = BlockType; |
| xfrmd.mm4_cmd_xfrmd_bits.dxfrdir = DataDirection; |
| xfrmd.mm4_cmd_xfrmd_bits.autocmd12 = FALSE; |
| xfrmd.mm4_cmd_xfrmd_bits.blkcbten = TRUE; |
| |
| #if SDIO_DMA |
| xfrmd.mm4_cmd_xfrmd_bits.dma_en = TRUE; |
| #endif |
| |
| EMMC_PRN(PRN_INFO,"\n %s, Sending command: xfrmd = 0x%x ",__func__, xfrmd.mm4_cmd_xfrmd_value); |
| |
| // Kick off the command |
| pContext->pMMC4Reg->mm4_cmd_xfrmd = xfrmd.mm4_cmd_xfrmd_value; |
| return; |
| } |
| |
| |
| /****************************************************************************** |
| Description: |
| Set up the registers of the controller to start the transaction to |
| communicate to the card for setup related commands. |
| The commands are clearly defined in the MMC specification. |
| Input Parameters: |
| pContext |
| Pointer to MMC context structure |
| Cmd |
| Command Index - See MMC or SD specification |
| argument |
| the argument of the command. MSW is for ARGH and LSW is for ARGL |
| ResType |
| Expected response type |
| Output Parameters: |
| None |
| Returns: |
| None |
| *******************************************************************************/ |
| void EMMCHC_SendSetupCommand(P_MM4_SDMMC_CONTEXT_T pContext, |
| UINT_T Cmd, |
| UINT_T CmdType, |
| UINT_T Argument, |
| UINT_T ResType) |
| { |
| MM4_CMD_XFRMD_UNION xfrmd; |
| P_MM4_STATE pMM4_STATE; |
| volatile uint32_t i = 0; |
| |
| // Make sure the controller is ready to accept the next command |
| pMM4_STATE = (P_MM4_STATE) &pContext->pMMC4Reg->mm4_state; |
| while (pMM4_STATE->ccmdinhbt) |
| {i++; if(i > 100) { break;} delay_ms(10);} // Wait. |
| |
| // Set the Argument Field |
| pContext->pMMC4Reg->mm4_arg = Argument; |
| |
| // Set the Data Transfer Command fields. |
| xfrmd.mm4_cmd_xfrmd_value = 0; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_idx = Cmd; |
| xfrmd.mm4_cmd_xfrmd_bits.cmd_type = CmdType; |
| if((ResType & 0x0000ff00) == MM4_RT_R3) |
| { |
| xfrmd.mm4_cmd_xfrmd_bits.idxchken = FALSE; |
| xfrmd.mm4_cmd_xfrmd_bits.crcchken = FALSE; |
| } |
| else if((ResType & 0x0000ff00) == MM4_RT_R2) |
| { |
| xfrmd.mm4_cmd_xfrmd_bits.idxchken = FALSE; |
| xfrmd.mm4_cmd_xfrmd_bits.crcchken = TRUE; |
| } |
| else |
| { |
| xfrmd.mm4_cmd_xfrmd_bits.idxchken = TRUE; |
| xfrmd.mm4_cmd_xfrmd_bits.crcchken = TRUE; |
| } |
| xfrmd.mm4_cmd_xfrmd_bits.res_type = ResType; |
| |
| EMMC_PRN(PRN_DBG,"\n Sending command: xfrmd = 0x%x ",xfrmd.mm4_cmd_xfrmd_value); |
| |
| // Kick off the command |
| pContext->pMMC4Reg->mm4_cmd_xfrmd = xfrmd.mm4_cmd_xfrmd_value; |
| return; |
| |
| } |
| |
| |
| |
| |
| /**************************************************************** |
| * EMMCHC_SetControllerVoltage |
| * Inspects the Capabilities Register for supported voltage types by the |
| * controller. Then programs the CNTL1 register with the desired range. |
| * Enables bus power |
| * Input: |
| * P_MM4_SDMMC_CONTEXT_T pContext |
| * Output: |
| * none |
| * Returns: |
| * none |
| *****************************************************************/ |
| UINT_T EMMCHC_SetControllerVoltage (P_MM4_SDMMC_CONTEXT_T pContext, UINT_T vcc) |
| { |
| UINT_T controllervoltage = 0; |
| P_MM4_CAP1_2 pMM4_CAP0 = (P_MM4_CAP1_2) &pContext->pMMC4Reg->mm4_cap1_2; |
| P_MM4_CNTL1 pMM4_CNTL1 = (P_MM4_CNTL1) &pContext->pMMC4Reg->mm4_cntl1; |
| MM4_CNTL1_UNION MM4_cntl1; |
| |
| |
| // Capture the Value |
| MM4_cntl1.mm4_cntl1_value = *(VUINT_T*) pMM4_CNTL1; |
| EMMC_PRN(PRN_DBG,"*pMM4_CNTL1 %x,\n",*pMM4_CNTL1); |
| EMMC_PRN(PRN_DBG,"*pMM4_CAP0 is %x, voltage supported bits: %d%d%d\n", |
| *pMM4_CAP0, pMM4_CAP0->vlg_33_support, |
| pMM4_CAP0->vlg_30_support, pMM4_CAP0->vlg_18_support); |
| |
| // Read the CAP0 register |
| if (pMM4_CAP0->vlg_33_support && (vcc == 3)) |
| controllervoltage = MM4_VLTGSEL_3_3; |
| else if (pMM4_CAP0->vlg_30_support && (vcc == 4)) |
| controllervoltage = MM4_VLTGSEL_3_0; |
| else if (pMM4_CAP0->vlg_18_support && (vcc == 1)) |
| controllervoltage = MM4_VLTGSEL_1_8; |
| else // default to 3.3V |
| controllervoltage = MM4_VLTGSEL_3_3; |
| |
| if(MM4_VLTGSEL_1_8 == controllervoltage) { |
| volatile P_MM4_ACMD12_ER pMM4_crtl2 = \ |
| (volatile P_MM4_ACMD12_ER) &pContext->pMMC4Reg->mm4_acmd12_er; |
| UINT_T data = *(VUINT_T*) pMM4_crtl2; |
| |
| data |= (0x1 << 19); // sgh_v18_en |
| *(VUINT_T*) pMM4_crtl2 = data; |
| EMMC_PRN(PRN_RES, "SDHCI: switch host to 1.8V ! \n"); |
| } |
| |
| //EMMC_PRN(PRN_INFO,"controllervoltage: %d\n",controllervoltage); |
| |
| // Set the voltage to controller |
| MM4_cntl1.mm4_cntl1_bits.vltgsel = controllervoltage; |
| |
| // Enable Bus Power |
| MM4_cntl1.mm4_cntl1_bits.buspwr = 1; |
| |
| // Write back out. |
| *(VUINT_T*) pMM4_CNTL1 = MM4_cntl1.mm4_cntl1_value; |
| EMMC_PRN(PRN_DBG,"*pMM4_CNTL1 %x,\n",*pMM4_CNTL1); |
| |
| return controllervoltage; |
| } |
| |
| void EMMCHC_EnableCmdInterrupt(P_MM4_SDMMC_CONTEXT_T pContext, unsigned int enable) |
| { |
| //unsigned int data; |
| P_MM4_I_STAT_EN pMM4_I_STAT_EN; |
| P_MM4_I_SIGN_EN pMM4_I_SIGN_EN; |
| MM4_I_STAT_UNION MM4_i_stat; |
| |
| |
| // Capture existing Value |
| pMM4_I_SIGN_EN = (P_MM4_I_SIGN_EN)((uintptr_t) &pContext->pMMC4Reg->mm4_i_sig_en); |
| pMM4_I_STAT_EN = (P_MM4_I_STAT_EN)((uintptr_t) &pContext->pMMC4Reg->mm4_i_stat_en); |
| |
| MM4_i_stat.mm4_i_stat_value = *(VUINT_T*)pMM4_I_STAT_EN; |
| // Route the interrupt signal enable register |
| MM4_i_stat.mm4_i_stat_bits.cmdcomp = enable&0x01; |
| |
| // EMMC_PRN(PRN_INFO," enable is %x MM4_i_stat.mm4_i_stat_bits.cmdcomp : %x\n", enable,MM4_i_stat.mm4_i_stat_bits.cmdcomp); |
| // Write it out |
| *(VUINT_T*)pMM4_I_STAT_EN = MM4_i_stat.mm4_i_stat_value; |
| *(VUINT_T*)pMM4_I_SIGN_EN = MM4_i_stat.mm4_i_stat_value; |
| |
| } |
| |
| void EMMCHC_cdInt_enable(P_MM4_SDMMC_CONTEXT_T pContext, UINT8_T Desire) |
| { |
| P_MM4_I_STAT_UNION pMM4_I_STAT_EN_U; |
| MM4_I_STAT_UNION stat_en_copy; |
| |
| |
| pMM4_I_STAT_EN_U = (P_MM4_I_STAT_UNION) &pContext->pMMC4Reg->mm4_i_stat_en; |
| stat_en_copy.mm4_i_stat_value= pMM4_I_STAT_EN_U->mm4_i_stat_value; |
| |
| if(Desire==1) |
| { |
| EMMC_PRN(PRN_INFO, "\n Interrupt Signal enable value is 0x%x",stat_en_copy.mm4_i_stat_value); |
| stat_en_copy.mm4_i_stat_bits.cdint = 1; |
| pMM4_I_STAT_EN_U->mm4_i_stat_value = stat_en_copy.mm4_i_stat_value; |
| stat_en_copy.mm4_i_stat_value= pMM4_I_STAT_EN_U->mm4_i_stat_value; |
| EMMC_PRN(PRN_INFO, "\n Interrupt Signal enable value is 0x%x",stat_en_copy.mm4_i_stat_value); |
| EMMC_PRN(PRN_RES, " Card Interrupt is enabled.\n "); |
| } |
| else |
| { |
| EMMC_PRN(PRN_INFO, "\n Interrupt Signal enable value is 0x%x",stat_en_copy); |
| stat_en_copy.mm4_i_stat_bits.cdint = 0; |
| pMM4_I_STAT_EN_U->mm4_i_stat_value = stat_en_copy.mm4_i_stat_value; |
| stat_en_copy.mm4_i_stat_value= pMM4_I_STAT_EN_U->mm4_i_stat_value; |
| EMMC_PRN(PRN_INFO, "\n Interrupt Signal enable value is 0x%x",stat_en_copy.mm4_i_stat_value); |
| EMMC_PRN(PRN_RES, " Card Interrupt is disabled.\n "); |
| } |
| return; |
| } |
| |