//
// 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_ch1.h"
#include <DrvLib_mem.h>

// Global Variables
LPCTSTR HostControllerName = TEXT("SDMMCCh1");
DWORD   HostControllerChannel = 1;
DWORD 	dwHostSupport8BitDataBus = 0;

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


BOOL CSDHControllerCh1::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_SDMMC1, sizeof(SDMMC_REG), FALSE);
    if (pSDMMC == NULL)
    {
        RETAILMSG(TRUE, (TEXT("[%s] SDMMC Special Register is *NOT* mapped.\n"), HostControllerName));
        return FALSE;
    }

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


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

    // SDMMC Ch1 initialization for "WakeUp"
    if (!InitCh())
    {
        return;
    }
    
    CSDHCBase::PowerUp();
}


extern "C" PCSDHCBase CreateSDMMCHCCh1Object()
{
    return new CSDHControllerCh1;
}


VOID CSDHControllerCh1::DestroySDMMCHCCh1Object(PCSDHCBase pSDHC)
{
    DEBUGCHK(pSDHC);
    delete pSDHC;
}


// The function that initilize SYSCON for a clock gating.
BOOL CSDHControllerCh1::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<<4)) | (7<<4);	  // Control MUXepll (0:FIN, 1:FOUT)
		pCLKPWR->CLK_SRC.CLK_SRC_MASK0 = (pCLKPWR->CLK_SRC.CLK_SRC_MASK0 & ~(1<<9)) | (1<<9);	  // unmask MUXMMC2 clock 
		pCLKPWR->CLK_DIV.CLK_DIV4 = (pCLKPWR->CLK_DIV.CLK_DIV4 & ~(0xf<<4)) | (0<<4);	 // no reason to divide
		pCLKPWR->CLK_GATE.CLK_GATE_IP2 = (pCLKPWR->CLK_GATE.CLK_GATE_IP2 & ~(0x1<<17)) | (0x1<<17);    // no reason to divide

		pCLKPWR->CLK_GATE.CLK_GATE_BLOCK = pCLKPWR->CLK_GATE.CLK_GATE_BLOCK | (0x1<<9);
#else
		pCLKPWR->CLK_SRC.CLK_SRC4 = (pCLKPWR->CLK_SRC.CLK_SRC4 & ~(0xf<<4)) | (6<<4);	  // Control MUXepll (0:FIN, 1:FOUT)
		pCLKPWR->CLK_SRC.CLK_SRC_MASK0 = (pCLKPWR->CLK_SRC.CLK_SRC_MASK0 & ~(1<<9)) | (1<<9);	  // unmask MUXMMC2 clock 
	pCLKPWR->CLK_DIV.CLK_DIV4 = (pCLKPWR->CLK_DIV.CLK_DIV4 & ~(0xf<<4)) | (0x7<<4);	 // Divide by 14 : 667M/14=47.64MHz
		pCLKPWR->CLK_GATE.CLK_GATE_IP2 = (pCLKPWR->CLK_GATE.CLK_GATE_IP2 & ~(0x1<<17)) | (0x1<<17);    // 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 CSDHControllerCh1::InitGPIO(void)
{
    DEBUGMSG(SDHC_CLOCK_ZONE,(TEXT("[%s] Setting registers for the GPIO.\r\n"), HostControllerName));
#if 0
    pIOPreg->GPG1.GP_PUD = pIOPreg->GPG2.GP_PUD & ~(0xFFFF);   // 8'b0010 for the SDMMC0: SD_0_CLK, SD_0_CMD, SD_0_DATA[5:0]
    pIOPreg->GPG1.GP_CON = 0x2222222;                      // Pull-up/down disabled
    pIOPreg->GPG1.GP_DRV_SR = (pIOPreg->GPG2.GP_DRV_SR & ~(0xFFFF)) |(0xFFFF);           // Drive strength = 4x

  
    if (SDMMC_CH1_WP == TRUE)
    {
		// add something.
    }
#else
	Set_PinPullUD(pIOPreg, MMC_SLOT0_B_CLK, 0x0); // pull up/down disalbe
	Set_PinPullUD(pIOPreg, MMC_SLOT0_B_CMD, 0x0); // pull up/down disalbe
	Set_PinPullUD(pIOPreg, MMC_SLOT0_B_CDn, 0x0); // pull up/down disalbe
	Set_PinPullUD(pIOPreg, MMC_SLOT0_B_D0, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT0_B_D1, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT0_B_D2, 0x2); // pull up enable due to no pull-up resistance on the smdk.
	Set_PinPullUD(pIOPreg, MMC_SLOT0_B_D3, 0x2); // pull up enable due to no pull-up resistance on the smdk.	

	Set_PinDrv(pIOPreg, MMC_SLOT0_B_CLK, 0x3); 
	Set_PinDrv(pIOPreg, MMC_SLOT0_B_CMD, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT0_B_CDn, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT0_B_D0, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT0_B_D1, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT0_B_D2, 0x3);
	Set_PinDrv(pIOPreg, MMC_SLOT0_B_D3, 0x3);

	Set_PinFunction(pIOPreg, MMC_SLOT0_B_CLK);
	Set_PinFunction(pIOPreg, MMC_SLOT0_B_CMD);	
	Set_PinFunction(pIOPreg, MMC_SLOT0_B_CDn);	
	Set_PinFunction(pIOPreg, MMC_SLOT0_B_D0);
	Set_PinFunction(pIOPreg, MMC_SLOT0_B_D1);
	Set_PinFunction(pIOPreg, MMC_SLOT0_B_D2);
	Set_PinFunction(pIOPreg, MMC_SLOT0_B_D3);
#endif

    return TRUE;
}


// The function that initilize the register for SDMMC Control.
BOOL CSDHControllerCh1::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 CLKMMC1
    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 CSDHControllerCh1::InitCh(void)
{
    if (!InitClkPwr())
    {
        return FALSE;
    }

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

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

    return TRUE;
}

