//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//

#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <register_map.h>
#include <cebuscfg.h>
#include <bsp.h>
#include "sdmmc_ch2.h" 
#include <DrvLib_mem.h>

// Global Variables
LPCTSTR HostControllerName    = TEXT("SDMMCCh2");
DWORD   HostControllerChannel = 2;
DWORD 	dwHostSupport8BitDataBus = 0;

static volatile CMU_CLK_REG *pCLKPWR;
static volatile GPIO_REG *pIOPreg;
static volatile SDMMC_REG *pSDMMC;


BOOL CSDHControllerCh2::Init(LPCTSTR pszActiveKey)
{
    DEBUGMSG(SDHC_CLOCK_ZONE,(TEXT("[%s] Initializing the SDMMC Host Controller\r\n"), HostControllerName));

    pCLKPWR = (volatile CMU_CLK_REG *)DrvLib_MapIoSpace(BASE_REG_PA_CMU_CLK, sizeof(CMU_CLK_REG), FALSE);
    if (pCLKPWR == NULL)
    {
        RETAILMSG(TRUE, (TEXT("[%s] Clock & Power Management Special Register is *NOT* mapped.\n"), HostControllerName));
        return FALSE;
    }
	
    pIOPreg = (volatile GPIO_REG *)DrvLib_MapIoSpace(BASE_REG_PA_GPIO, sizeof(GPIO_REG), FALSE);
    if (pIOPreg == NULL)
    {
        RETAILMSG(TRUE, (TEXT("[%s] GPIO registers is *NOT* mapped.\n"), HostControllerName));
        return FALSE;
    }

    pSDMMC = (volatile SDMMC_REG *)DrvLib_MapIoSpace(BASE_REG_PA_SDMMC2, sizeof(SDMMC_REG), FALSE);
    if (pSDMMC == NULL)
    {
        RETAILMSG(TRUE, (TEXT("[%s] SDMMC Special Register is *NOT* mapped.\n"), HostControllerName));
        return FALSE;
    }

    // SDMMC Ch2 initialization
    if (!InitCh())
    {
        return FALSE;
    }
    
    return CSDHCBase::Init(pszActiveKey);
}


VOID CSDHControllerCh2::PowerUp(void)
{
    DEBUGMSG(SDHC_CLOCK_ZONE,(TEXT("[%s] Power Up the SDMMC Host Controller\r\n"), HostControllerName));

    // SDMMC Ch2 initialization for "WakeUp"
    if (!InitCh())
    {
        return;
    }

    CSDHCBase::PowerUp();
}


extern "C" PCSDHCBase CreateSDMMCHCCh2Object()
{
    return new CSDHControllerCh2;
}


VOID CSDHControllerCh2::DestroySDMMCHCCh2Object(PCSDHCBase pSDHC)
{
    DEBUGCHK(pSDHC);
    delete pSDHC;
}


// The function that initilize SYSCON for a clock gating.
BOOL CSDHControllerCh2::InitClkPwr(void)
{
    DEBUGMSG(SDHC_CLOCK_ZONE,(TEXT("[%s] Setting registers for the EPLL (for SDCLK) : SYSCon.\r\n"), HostControllerName));

#if 0
	pCLKPWR->CLK_SRC.CLK_SRC4 = (pCLKPWR->CLK_SRC.CLK_SRC4 & ~(0xf<<8)) | (7<<8);	  // Control MUXepll (0:FIN, 1:FOUT)
	pCLKPWR->CLK_SRC.CLK_SRC_MASK0 = (pCLKPWR->CLK_SRC.CLK_SRC_MASK0 & ~(1<<10)) | (1<<10); 	// unmask MUXMMC2 clock 
	pCLKPWR->CLK_DIV.CLK_DIV4 = (pCLKPWR->CLK_DIV.CLK_DIV4 & ~(0xf<<8)) | (0<<8);	 // no reason to divide
	pCLKPWR->CLK_GATE.CLK_GATE_IP2 = (pCLKPWR->CLK_GATE.CLK_GATE_IP2 & ~(0x1<<18)) | (0x1<<18);    // no reason to divide

	pCLKPWR->CLK_GATE.CLK_GATE_BLOCK = pCLKPWR->CLK_GATE.CLK_GATE_BLOCK | (0x1<<9);

	DrvLib_UnmapIoSpace((PVOID)pCLKPWR);
#else
	pCLKPWR->CLK_SRC.CLK_SRC4 = (pCLKPWR->CLK_SRC.CLK_SRC4 & ~(0xf<<8)) | (6<<8);	  // Control MUXepll (0:FIN, 1:FOUT)
	pCLKPWR->CLK_SRC.CLK_SRC_MASK0 = (pCLKPWR->CLK_SRC.CLK_SRC_MASK0 & ~(1<<10)) | (1<<10);	  // unmask MUXMMC2 clock 
	pCLKPWR->CLK_DIV.CLK_DIV4 = (pCLKPWR->CLK_DIV.CLK_DIV4 & ~(0xf<<8)) | (7<<8);	// divide by 7
	pCLKPWR->CLK_GATE.CLK_GATE_IP2 = (pCLKPWR->CLK_GATE.CLK_GATE_IP2 & ~(0x1<<18)) | (0x1<<18);    // no reason to divide

	pCLKPWR->CLK_GATE.CLK_GATE_BLOCK = pCLKPWR->CLK_GATE.CLK_GATE_BLOCK | (0x1<<9);
#endif

    return TRUE;
}


// The function that initilize GPIO for DAT, CD and WP lines.
BOOL CSDHControllerCh2::InitGPIO(void)
{
    DEBUGMSG(SDHC_CLOCK_ZONE,(TEXT("[%s] Setting registers for the GPIO.\r\n"), HostControllerName));

#if 0
    pIOPreg->GPG2.GP_PUD = pIOPreg->GPG2.GP_PUD & ~(0xFFFF);   // 8'b0010 for the SDMMC0: SD_0_CLK, SD_0_CMD, SD_0_DATA[5:0]
    pIOPreg->GPG2.GP_CON = 0x2222222;                      // Pull-up/down disabled
    pIOPreg->GPG2.GP_DRV_SR = (pIOPreg->GPG2.GP_DRV_SR & ~(0xFFFF)) |(0xFFFF);           // Drive strength = 4x


    pIOPreg->GPG3.GP_PUD = pIOPreg->GPG2.GP_PUD & ~(0xFF<<6);   // 8'b0010 for the SDMMC0: SD_0_CLK, SD_0_CMD, SD_0_DATA[5:0]
    pIOPreg->GPG3.GP_CON = (pIOPreg->GPG3.GP_CON & ~(0xffff<<12)) | (0x3333<<12);                      // Pull-up/down disabled
    pIOPreg->GPG3.GP_DRV_SR &= (~(0xff<<6) | (0xff));           // Drive strength = 4x
  
    if (SDMMC_CH2_WP == TRUE)
    {
    }
#else
	Set_PinPullUD(pIOPreg, MMC_SLOT1_CLK, 0x0); // pull up/down disalbe
	Set_PinPullUD(pIOPreg, MMC_SLOT1_CMD, 0x0); // pull up/down disalbe
	Set_PinPullUD(pIOPreg, MMC_SLOT1_CDn, 0x0); // pull up/down disalbe
	Set_PinPullUD(pIOPreg, MMC_SLOT1_D0, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT1_D1, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT1_D2, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT1_D3, 0x2); // pull up enable due to no pull-up resistance on the smdk.	

	Set_PinDrv(pIOPreg, MMC_SLOT1_CLK, 0x3); 
	Set_PinDrv(pIOPreg, MMC_SLOT1_CMD, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_CDn, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_D0, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_D1, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_D2, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_D3, 0x3);


	Set_PinFunction(pIOPreg, MMC_SLOT1_CLK);
	Set_PinFunction(pIOPreg, MMC_SLOT1_CMD);	
	Set_PinFunction(pIOPreg, MMC_SLOT1_CDn);	
	Set_PinFunction(pIOPreg, MMC_SLOT1_D0);
	Set_PinFunction(pIOPreg, MMC_SLOT1_D1);
	Set_PinFunction(pIOPreg, MMC_SLOT1_D2);
	Set_PinFunction(pIOPreg, MMC_SLOT1_D3);
#endif

#ifdef _USE_SDMMC_8BIT_
	dwHostSupport8BitDataBus = TRUE;

	Set_PinPullUD(pIOPreg, MMC_SLOT1_D4, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT1_D5, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT1_D6, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT1_D7, 0x2); // pull up enable due to no pull-up resistance on the smdk.	
	
	Set_PinDrv(pIOPreg, MMC_SLOT1_D4, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_D5, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_D6, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT1_D7, 0x3);
	
	Set_PinFunction(pIOPreg, MMC_SLOT1_D4);
	Set_PinFunction(pIOPreg, MMC_SLOT1_D5);
	Set_PinFunction(pIOPreg, MMC_SLOT1_D6);
	Set_PinFunction(pIOPreg, MMC_SLOT1_D7);		
#endif
    return TRUE;
}


// The function that initilize the register for SDMMC Control.
BOOL CSDHControllerCh2::InitSDMMC(void)
{
    DEBUGMSG(SDHC_CLOCK_ZONE,(TEXT("[%s] Setting registers for the EPLL : SDMMCCon.\r\n"), HostControllerName));

    // Set the clock source to EPLL out for CLKMMC0
    pSDMMC->CONTROL2 = (
            SDHC_CONTROL2_ENCMDCNFMSK |     // Command Conflict Mask Enable
            SDHC_CONTROL2_DFCNT |           // Debounce Filter Count : 64 iSDCLK
            SDHC_CONTROL2_ENCLKOUTHOLD |    // SDCLK Hold Enable
            SDHC_CONTROL2_BASECLK);         // Base Clock Source = EPLL out
    return TRUE;
}


BOOL CSDHControllerCh2::InitCh(void)
{
    if (!InitClkPwr())
    {
        return FALSE;
    }

    if (!InitGPIO())
    {
        return FALSE;
    }

    if (!InitSDMMC())
    {
        return FALSE;
    }

    return TRUE;
}

