/*
 * Copyright 2005 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file clock.c
 * @brief API for setting up and retrieving clocks.
 *
 * This file contains API for setting up and retrieving clocks.
 *
 * @ingroup CLOCKS
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/arch/clock.h>

/*!
 * Spinlock to protect CRM register accesses
 */
static spinlock_t mxc_crm_lock = SPIN_LOCK_UNLOCKED;

static unsigned long crm_ap_base = (unsigned long)(IO_ADDRESS(CRM_AP_BASE_ADDR));

static unsigned long pll_base[] = {
        (unsigned long) (IO_ADDRESS(PLL0_BASE_ADDR)),
        (unsigned long) (IO_ADDRESS(PLL1_BASE_ADDR)),
        (unsigned long) (IO_ADDRESS(PLL2_BASE_ADDR)),
};

static unsigned long CRM_SMALL_DIV[] = {2, 3, 4, 5, 6, 8, 10, 12};

/*!
 * This function is used to set the PLL pdf value into both dp_op registers
 *
 * @param pll_num   the PLL that you wish to modify
 * @param pdf       pre-division factor
 */
void mxc_pll_pdf_set(enum plls pll_num, unsigned int pdf)
{
        volatile unsigned long dp_op;
        unsigned long pllbase, flags;

        pllbase = pll_base[pll_num];
        spin_lock_irqsave(&mxc_crm_lock, flags);
        dp_op = readl(pllbase + MXC_PLL_DP_OP);
        dp_op = (dp_op & (~MXC_PLL_DP_OP_PDF_MASK)) | pdf;
        writel(dp_op, pll_base + MXC_PLL_DP_OP);

        dp_op = readl(pllbase + MXC_PLL_DP_HFS_OP);
        dp_op = (dp_op & (~MXC_PLL_DP_OP_PDF_MASK)) | pdf;
        writel(dp_op, pll_base + MXC_PLL_DP_HFS_OP);
        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

/*!
 * This function is used to modify PLL control register.
 * @param  pll_num  the PLL that you wish to modify
 * @param  reg_val  value to write into the control register
 */
void mxc_pll_dpctl_set(enum plls pll_num, unsigned long reg_val)
{
        volatile unsigned long dp_ctl;
        unsigned long pllbase, flags;
        
        pllbase = pll_base[pll_num];
        spin_lock_irqsave(&mxc_crm_lock, flags);
        dp_ctl = readl(pllbase + MXC_PLL_DP_CTL);
        dp_ctl |= reg_val;
        writel(dp_ctl, pllbase + MXC_PLL_DP_CTL);
        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

/*!
 * This function is used to modify PLL registers to generate the required
 * frequency.
 *
 * @param  pll_num  the PLL that you wish to modify
 * @param  mfi      multiplication factor integer part
 * @param  pdf      pre-division factor
 * @param  mfd      multiplication factor denominator
 * @param  mfn      multiplication factor numerator
 */
void mxc_pll_set(enum plls pll_num, unsigned int mfi, unsigned int pdf,
                 unsigned int mfd, unsigned int mfn)
{
        volatile unsigned long acsr, pll_hfsm, dp_op;
        unsigned int dp_op_off, mfn_off, mfd_off, pllbase, flags;

	BUG_ON(pll_num > (sizeof(pll_base)/sizeof(pll_base[0])));

        pllbase = pll_base[pll_num];

        spin_lock_irqsave(&mxc_crm_lock, flags);
        /* switch to ap_ref_clk */
        acsr = readl(crm_ap_base + MXC_CRMAP_ACSR);
        acsr &= ~MXC_CRMAP_ACSR_ACS;
        writel(acsr, crm_ap_base + MXC_CRMAP_ACSR);

        /* adjust pll settings */
        pll_hfsm = readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_HFSM;
        if (pll_hfsm == MXC_PLL_DP_CTL_HFSM) {
                dp_op_off = MXC_PLL_DP_HFS_OP;
                mfd_off = MXC_PLL_DP_HFS_MFD;
                mfn_off = MXC_PLL_DP_HFS_MFN;
        } else {
                dp_op_off = MXC_PLL_DP_OP;
                mfd_off = MXC_PLL_DP_MFD;
                mfn_off = MXC_PLL_DP_MFN;
        }
                
        if ((mfi != NO_UPDATE) && (pdf != NO_UPDATE)) {
                writel(mfi << MXC_PLL_DP_OP_MFI_OFFSET | pdf, 
                       pllbase + dp_op_off);
        } else {
                dp_op = readl(pllbase + dp_op_off);
                if (pdf != NO_UPDATE) {
                        dp_op = (dp_op & (~MXC_PLL_DP_OP_PDF_MASK)) | pdf;
                } else {
                        dp_op = (dp_op & (~MXC_PLL_DP_OP_MFI_MASK)) | 
                                (mfi << MXC_PLL_DP_OP_MFI_OFFSET);
                }
                writel(dp_op, pllbase + dp_op_off);
        }
        if (mfd != NO_UPDATE) {
                writel(mfd, pllbase + mfd_off);
        }
        if (mfn != NO_UPDATE) {
                writel(mfn, pllbase + mfn_off);
        }

        /* switch back to pll */
        acsr |= MXC_CRMAP_ACSR_ACS;
        writel(acsr, crm_ap_base + MXC_CRMAP_ACSR);
        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

/*!
 * This function is used to get PLL registers values used generate the clock
 * frequency.
 *
 * @param  pll_num  the PLL that you wish to access
 * @param  mfi      pointer that holds multiplication factor integer part
 * @param  pdf      pointer that holds pre-division factor
 * @param  mfd      pointer that holds multiplication factor denominator
 * @param  mfn      pointer that holds multiplication factor numerator
 */
void mxc_pll_get(enum plls pll_num, unsigned int *mfi, unsigned int *pdf,
                 unsigned int *mfd, unsigned int *mfn)
{
        volatile unsigned long pll_hfsm, pll_dp_op;
        unsigned long pllbase, flags;
                                                                                        
	BUG_ON(pll_num > (sizeof(pll_base)/sizeof(pll_base[0])));
                                                                                        
        pllbase = pll_base[pll_num];

        spin_lock_irqsave(&mxc_crm_lock, flags);

        pll_hfsm = readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_HFSM;

        if (pll_hfsm == MXC_PLL_DP_CTL_HFSM) {
                pll_dp_op = readl(pllbase + MXC_PLL_DP_HFS_OP);
                *pdf = pll_dp_op & MXC_PLL_DP_OP_PDF_MASK;
                *mfi = pll_dp_op >> MXC_PLL_DP_OP_MFI_OFFSET;
                *mfd = readl(pllbase + MXC_PLL_DP_HFS_MFD); 
                *mfn = readl(pllbase + MXC_PLL_DP_HFS_MFN); 
        } else {
                pll_dp_op = readl(pllbase + MXC_PLL_DP_OP);
                *pdf = pll_dp_op & MXC_PLL_DP_OP_PDF_MASK;
                *mfi = pll_dp_op >> MXC_PLL_DP_OP_MFI_OFFSET;
                *mfd = readl(pllbase + MXC_PLL_DP_MFD); 
                *mfn = readl(pllbase + MXC_PLL_DP_MFN); 
        }

        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

/*!
 * This function returns the PLL output value in Hz based on pll.
 * @param       pll_num     PLL as defined in enum plls
 * @return      PLL value in Hz.
 */
unsigned long mxc_pll_clock(enum plls pll_num)
{
        signed long mfi, mfn, mfd, pdf, ref_clk, pll_out;
        volatile unsigned long dp_op, dp_mfd, dp_mfn, pll_hfsm;
        unsigned long pllbase, flags;

	BUG_ON(pll_num > (sizeof(pll_base)/sizeof(pll_base[0])));

        pllbase = pll_base[pll_num];

        spin_lock_irqsave(&mxc_crm_lock, flags);

        pll_hfsm = readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_HFSM;
        if (pll_hfsm == 0) {
                dp_op = readl(pllbase + MXC_PLL_DP_OP);
                dp_mfd = readl(pllbase + MXC_PLL_DP_MFD);
                dp_mfn = readl(pllbase + MXC_PLL_DP_MFN);
        } else {
                dp_op = readl(pllbase + MXC_PLL_DP_HFS_OP);
                dp_mfd = readl(pllbase + MXC_PLL_DP_HFS_MFD);
                dp_mfn = readl(pllbase + MXC_PLL_DP_HFS_MFN);
        }
        pdf = (signed long) (dp_op & MXC_PLL_DP_OP_PDF_MASK);
        mfi = (signed long) ((dp_op >> MXC_PLL_DP_OP_MFI_OFFSET) & 
                              MXC_PLL_DP_OP_PDF_MASK);
        mfi = (mfi <= 5) ? 5: mfi;
        mfd = (signed long) (dp_mfd & MXC_PLL_DP_MFD_MASK);
        mfn = (signed long) (dp_mfn & MXC_PLL_DP_MFN_MASK);
        mfn = (mfn <= 0x4000000) ? mfn : (mfn - 0x10000000);

        ref_clk = MXC_PLL_REF_CLK;

        pll_out = (2 * ref_clk * mfi + ((2 * ref_clk / (mfd + 1)) * mfn)) /
                  (pdf + 1);

        spin_unlock_irqrestore(&mxc_crm_lock, flags);

        return pll_out;
}

/*!
 * This function returns the main clock dividers.
 * @param       clk     peripheral clock as defined in enum mxc_clocks
 *
 * @return      divider value
 */
unsigned long mxc_main_clock_divider(enum mxc_clocks clk)
{
        unsigned long div = 0;
        volatile unsigned long acdr, acder2;

        acdr = readl(crm_ap_base + MXC_CRMAP_ACDR);
        acder2 = readl(crm_ap_base + MXC_CRMAP_ACDER2);

        switch (clk) {
        case CPU_CLK:
                div = (acdr & MXC_CRMAP_ACDR_ARMDIV_MASK) >> 
                       MXC_CRMAP_ACDR_ARMDIV_OFFSET;
                div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
                break;
        case AHB_CLK:
                div = (acdr & MXC_CRMAP_ACDR_AHBDIV_MASK) >> 
                       MXC_CRMAP_ACDR_AHBDIV_OFFSET;
                div = (div == 0) ? 16 : div;
                break;
        case IPG_CLK:
                div = acdr & MXC_CRMAP_ACDR_IPDIV_MASK;
                div = (div == 0) ? 16 : div;
                break;
        case NFC_CLK:
                div = ((acder2 & MXC_CRMAP_ACDER2_NFCDIV_MASK) >> 
                        MXC_CRMAP_ACDER2_NFCDIV_OFFSET) + 1;
                break;
        case USB_CLK:
                div = (acder2 & MXC_CRMAP_ACDER2_USBDIV_MASK) >> 
                       MXC_CRMAP_ACDER2_USBDIV_OFFSET;
                div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
                break;
        default:
                printk("Wrong clock: %d\n", clk);
                break;
        }

        return div;
}

/*!
 * This function returns the peripheral clock dividers.
 * Note that for SSI divider, in order to maintain the accuracy, the returned
 * divider is doubled.
 *
 * @param       clk     peripheral clock as defined in enum mxc_clocks
 * @return      divider value
 */
unsigned long mxc_peri_clock_divider(enum mxc_clocks clk)
{
        unsigned long div = 0;
        volatile unsigned long apra, acder1, acder2;

        apra = readl(crm_ap_base + MXC_CRMAP_APRA);
        acder1 = readl(crm_ap_base + MXC_CRMAP_ACDER1);
        acder2 = readl(crm_ap_base + MXC_CRMAP_ACDER2);

        switch (clk) {
        case UART1_BAUD:
        case UART2_BAUD:
                div = acder2 & MXC_CRMAP_ACDER2_BAUDDIV_MASK;
                div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
                break;
        case UART3_BAUD:
                div = (apra & MXC_CRMAP_APRA_UART3DIV_MASK) >> 
                       MXC_CRMAP_APRA_UART3DIV_OFFSET;
                div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
                break;
        case SSI1_BAUD:
                div = acder1 & MXC_CRMAP_ACDER1_SSI1DIV_MASK;
                /* Return twice the divider to avoid FP */
                div = (div == 0 || div == 1) ? (62 * 2): div;
                break;
        case SSI2_BAUD:
                div = (acder1 & MXC_CRMAP_ACDER1_SSI2DIV_MASK) >> 
                       MXC_CRMAP_ACDER1_SSI2DIV_OFFSET;
                /* Return twice the divider to avoid FP */
                div = (div == 0 || div == 1) ? (62 * 2): div;
                break;
        case CSI_BAUD:
                div = (acder1 & MXC_CRMAP_ACDER1_CSIDIV_MASK) >>
                       MXC_CRMAP_ACDER1_CSIDIV_OFFSET;
                div = (div > 7) ? 1 : (CRM_SMALL_DIV[div]);
                break;
        default:
                printk("Wrong clock: %d\n", clk);
                break;
        }

        return div;
}

/*!
 * This function returns various reference clocks.
 *
 * @param       ap_unc_pat_ref     pointer to ap_uncorrected_pat_ref value
 *                                 on return
 * @param       ap_ref_x2          pointer to ap_ref_x2 value on return
 * @param       ap_ref             pointer to ap_ref value on return
 *
 * @return      clock value in Hz
 */
void mxc_get_ref_clk(unsigned long *ap_unc_pat_ref, unsigned long *ap_ref_x2,
                     unsigned long *ap_ref)
{
        unsigned long ap_pat_ref_div_1, ap_pat_ref_div_2, ap_isel;
        volatile unsigned long acsr, ascsr, adcr, acder2;

        acsr = readl(crm_ap_base + MXC_CRMAP_ACSR);
        ascsr = readl(crm_ap_base + MXC_CRMAP_ASCSR);
        adcr = readl(crm_ap_base + MXC_CRMAP_ADCR);
        acder2 = readl(crm_ap_base + MXC_CRMAP_ACDER2);

        ap_isel = ascsr & MXC_CRMAP_ASCSR_APISEL;
        ap_pat_ref_div_1 = ((ascsr >> MXC_CRMAP_ASCSR_AP_PATDIV1_OFFSET) & 0x1) + 1;
        ap_pat_ref_div_2 = ((ascsr >> MXC_CRMAP_ASCSR_AP_PATDIV2_OFFSET) & 0x1) + 1;

        *ap_unc_pat_ref = MXC_PLL_REF_CLK * (ap_isel + 1);
        *ap_ref_x2 =  (*ap_unc_pat_ref)/ ap_pat_ref_div_1;
        *ap_ref = (*ap_ref_x2) / ap_pat_ref_div_2;
}

/*!
 * This function returns the clock value in Hz for various MXC modules.
 *
 * @param       clk     as defined in enum mxc_clocks
 *
 * @return      clock value in Hz
 */
unsigned long mxc_get_clocks(enum mxc_clocks clk)
{
        volatile unsigned long apra, acsr, ascsr, adcr, acder1, acder2; 
        unsigned long ap_unc_pat_ref, sel, ret_val = 0, apsel, 
                      ap_clk_pre_dfs, lfdf = 1, ap_ref_x2_clk, ap_ref_clk;

        apra = readl(crm_ap_base + MXC_CRMAP_APRA);
        acsr = readl(crm_ap_base + MXC_CRMAP_ACSR);
        ascsr = readl(crm_ap_base + MXC_CRMAP_ASCSR);
        adcr = readl(crm_ap_base + MXC_CRMAP_ADCR);
        acder1 = readl(crm_ap_base + MXC_CRMAP_ACDER1);
        acder2 = readl(crm_ap_base + MXC_CRMAP_ACDER2);

        mxc_get_ref_clk(&ap_unc_pat_ref, &ap_ref_x2_clk, &ap_ref_clk);

        if ((acsr & MXC_CRMAP_ACSR_ACS) == 0) {
                // inverted pat_ref is selected
                ap_clk_pre_dfs = ap_ref_clk;
        } else {
                // Now AP domain runs off the pll
                apsel = (ascsr & MXC_CRMAP_ASCSR_APSEL_MASK) >> 
                         MXC_CRMAP_ASCSR_APSEL_OFFSET;
                ap_clk_pre_dfs = mxc_pll_clock(apsel) / 
                                 mxc_main_clock_divider(CPU_CLK);
        }

        switch (clk) {
        case CPU_CLK:
                if ((adcr & MXC_CRMAP_ADCR_DIV_BYP) == 0) {
                        // DFS divider used
                        lfdf = mxc_get_lfdf_value();
                }
                ret_val = ap_clk_pre_dfs / lfdf;
                break;
        case AHB_CLK:
        case IPU_CLK:
                ret_val = ap_clk_pre_dfs / mxc_main_clock_divider(AHB_CLK);
                break;
        case NFC_CLK:
                if ((acder2 & MXC_CRMAP_ACDER2_NFCEN) == 0){
                        printk("Warning: NFC clock is not enabled !!!\n");
                } else {
                        ret_val = ap_clk_pre_dfs / (mxc_main_clock_divider(AHB_CLK) *
                                                    mxc_main_clock_divider(NFC_CLK));
                }
                break;
        case USB_CLK:
                if ((acder2 & MXC_CRMAP_ACDER2_USBEN) == 0){
                        printk("Warning: USB clock is not enabled !!!\n");
                } else {
                        if ((acsr & MXC_CRMAP_ACSR_ACS) == 0) {
                                // inverted pat_ref is selected
                                ret_val = ap_ref_clk / 8;
                        } else {
                                sel = (ascsr & MXC_CRMAP_ASCSR_USBSEL_MASK) >> 
                                       MXC_CRMAP_ASCSR_USBSEL_OFFSET;
                                ret_val = mxc_pll_clock(sel) /
                                          mxc_main_clock_divider(USB_CLK);
                        }
                }
                break;
        case UART1_BAUD:
                ret_val = ap_unc_pat_ref /
                          mxc_peri_clock_divider(UART1_BAUD);
                break;
        case UART2_BAUD:
        case SDHC1_CLK:
        case SDHC2_CLK:
                ret_val = ap_unc_pat_ref /
                          mxc_peri_clock_divider(UART2_BAUD);
                break;
        case UART3_BAUD:
                ret_val = ap_unc_pat_ref /
                          mxc_peri_clock_divider(UART3_BAUD);
                break;
        case SSI1_BAUD:
                if ((acder1 & MXC_CRMAP_ACDER1_SSI1EN) == 0) {
                        printk("Warning: SSI 1 clock is not enabled !!!\n");
                } else {
                        if ((acsr & MXC_CRMAP_ACSR_ACS) == 0) {
                                // inverted pat_ref is selected
                                ret_val = ap_ref_clk / 8;
                        } else {
                                sel = (ascsr & MXC_CRMAP_ASCSR_SSI1SEL_MASK) >> 
                                       MXC_CRMAP_ASCSR_SSI1SEL_OFFSET;
                                /* Don't forget to double the numerator */
                                ret_val = (2 * mxc_pll_clock(sel)) /
                                          mxc_peri_clock_divider(SSI1_BAUD);
                        }
                }
                break;
        case SSI2_BAUD:
                if ((acder1 & MXC_CRMAP_ACDER1_SSI2EN) == 0) {
                        printk("Warning: SSI 2 clock is not enabled !!!\n");
                } else {
                        if ((acsr & MXC_CRMAP_ACSR_ACS) == 0) {
                                // inverted pat_ref is selected
                                ret_val = ap_ref_clk / 8;
                        } else {
                                sel = (ascsr & MXC_CRMAP_ASCSR_SSI2SEL_MASK) >> 
                                       MXC_CRMAP_ASCSR_SSI2SEL_OFFSET;
                                /* Don't forget to double the numerator */
                                ret_val = (2 * mxc_pll_clock(sel)) /
                                          mxc_peri_clock_divider(SSI2_BAUD);
                        }
                }
                break;
        case CSI_BAUD:
                if ((acder1 & MXC_CRMAP_ACDER1_CSIEN) == 0) {
                        printk("Warning: CSI clock is not enabled !!!\n");
                } else {
                        if ((acsr & MXC_CRMAP_ACSR_ACS) == 0) {
                                // inverted pat_ref is selected
                                ret_val = ap_ref_clk / 8;
                        } else {
                                sel = (ascsr & MXC_CRMAP_ASCSR_CSISEL_MASK) >> 
                                       MXC_CRMAP_ASCSR_CSISEL_OFFSET;
                                ret_val = mxc_pll_clock(sel) / 
                                          (mxc_peri_clock_divider(CSI_BAUD));
                        }
                }
                break;
        case I2C_CLK:
                if ((acsr & MXC_CRMAP_ACSR_ACS) == 0) {
                        // inverted pat_ref is selected
                        ret_val = ap_ref_clk / 4;
                } else {
                        ret_val = ap_ref_clk;
                }
                break;
        case CSPI1_CLK:
        case CSPI2_CLK:
        case GPT_CLK:
        case RTC_CLK:
        case EPIT1_CLK:
        case EDIO_CLK:
        case WDOG_CLK:
        case PWM_CLK:
        case SIM1_CLK:
        case HAC_CLK:
        case GEM_CLK:
        case SDMA_CLK:
        case RNG_CLK:
        case KPP_CLK:
        case MU_CLK:
        case RTIC_CLK:
        case SCC_CLK:
        case SPBA_CLK:
        case DSM_CLK:
        case IPG_CLK:
                ret_val = ap_clk_pre_dfs / mxc_main_clock_divider(IPG_CLK);
                break;
        }

        return ret_val;
}

/*!
 * This function sets the PLL source for a clock.
 *
 * @param clk     as defined in enum mxc_clocks
 * @param pll_num the PLL that you wish to use as source for this clock
 */
void mxc_set_clocks_pll(enum mxc_clocks clk, enum plls pll_num)
{
        volatile unsigned long ascsr;
        unsigned long flags;

        spin_lock_irqsave(&mxc_crm_lock, flags);
        ascsr = readl(crm_ap_base + MXC_CRMAP_ASCSR);
        switch (clk) {
        case CPU_CLK:
                ascsr = (ascsr & (~MXC_CRMAP_ASCSR_APSEL_MASK)) | 
                        (pll_num << MXC_CRMAP_ASCSR_APSEL_OFFSET); 
                break;
        case USB_CLK:
                ascsr = (ascsr & (~MXC_CRMAP_ASCSR_USBSEL_MASK)) | 
                        (pll_num << MXC_CRMAP_ASCSR_USBSEL_OFFSET); 
                break;
        case SSI1_BAUD:
                ascsr = (ascsr & (~MXC_CRMAP_ASCSR_SSI1SEL_MASK)) | 
                        (pll_num << MXC_CRMAP_ASCSR_SSI1SEL_OFFSET); 
                break;
        case SSI2_BAUD:
                ascsr = (ascsr & (~MXC_CRMAP_ASCSR_SSI2SEL_MASK)) | 
                        (pll_num << MXC_CRMAP_ASCSR_SSI2SEL_OFFSET); 
                break;
        case CSI_BAUD:
                ascsr = (ascsr & (~MXC_CRMAP_ASCSR_CSISEL_MASK)) | 
                        (pll_num << MXC_CRMAP_ASCSR_CSISEL_OFFSET); 
                break;
        default:
                printk("This clock does not have ability to choose the PLL\n");
                break;
        }
        writel(ascsr, crm_ap_base + MXC_CRMAP_ASCSR);
        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

/*!
 * This function sets the division factor for a clock.
 *
 * @param clk as defined in enum mxc_clocks
 * @param div the division factor to be used for the clock (For SSI & CSI, pass in
 *            2 times the expected division value to account for FP vals)
 */
void mxc_set_clocks_div(enum mxc_clocks clk, unsigned int div)
{
        volatile unsigned long reg;
        unsigned long flags;
        unsigned int d;

        spin_lock_irqsave(&mxc_crm_lock, flags);

        switch (clk) {
        case AHB_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDR);
                d = (div == 16) ? 0 : div;
                reg = (reg & (~MXC_CRMAP_ACDR_AHBDIV_MASK)) |
                      (d << MXC_CRMAP_ACDR_AHBDIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDR);
                break;
        case IPG_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDR);
                d = (div == 16) ? 0 : div;
                reg = (reg & (~MXC_CRMAP_ACDR_IPDIV_MASK)) |
                      (d << MXC_CRMAP_ACDR_IPDIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDR);
                break;
        case NFC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER2);
                reg = (reg & (~MXC_CRMAP_ACDER2_NFCDIV_MASK)) |
                      ((div - 1) << MXC_CRMAP_ACDER2_NFCDIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER2);
                break;
        case CPU_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDR);
                d = (div <= 1) ? 15 : (div - 2);
                if (div > 6) {
                        d = d - ((div - 6) / 2);
                }
                reg = (reg & (~MXC_CRMAP_ACDR_ARMDIV_MASK)) |
                      (d << MXC_CRMAP_ACDR_ARMDIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDR);
                break;
        case USB_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER2);
                d = (div <= 1) ? 15 : (div - 2);
                if (div > 6) {
                        d = d - ((div - 6) / 2);
                }
                reg = (reg & (~MXC_CRMAP_ACDER2_USBDIV_MASK)) |
                      (d << MXC_CRMAP_ACDER2_USBDIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER2);
                break;
        case UART1_BAUD:
        case UART2_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER2);
                d = (div <= 1) ? 15 : (div - 2);
                if (div > 6) {
                        d = d - ((div - 6) / 2);
                }
                reg = (reg & (~MXC_CRMAP_ACDER2_BAUDDIV_MASK)) | d;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER2);
                break;
        case UART3_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                d = (div <= 1) ? 15 : (div - 2);
                if (div > 6) {
                        d = d - ((div - 6) / 2);
                }
                reg = (reg & (~MXC_CRMAP_APRA_UART3DIV_MASK)) |
                      (d << MXC_CRMAP_APRA_UART3DIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case SSI1_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                d = (div == 62 * 2) ? 0 : div;
                reg = (reg & (~MXC_CRMAP_ACDER1_SSI1DIV_MASK)) |
                      (d << MXC_CRMAP_ACDER1_SSI1DIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break;
        case SSI2_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                d = (div == 62 * 2) ? 0 : div;
                reg = (reg & (~MXC_CRMAP_ACDER1_SSI2DIV_MASK)) |
                      (d << MXC_CRMAP_ACDER1_SSI2DIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break;
        case CSI_BAUD:
                div = div / 2;
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                d = (div <= 1) ? 15 : (div - 2);
                if (div > 6) {
                        d = d - ((div - 6) / 2);
                }
                reg = (reg & (~MXC_CRMAP_ACDER1_CSIDIV_MASK)) |
                      (d << MXC_CRMAP_ACDER1_CSIDIV_OFFSET);
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break;
        default:
                break;
        }

        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

/*!
 * This function returns the DFS block divider - LFDF value
 *
 * @return      Low Voltage frequency Divider Factor value
 */
unsigned int mxc_get_lfdf_value(void)
{
        volatile unsigned long reg = readl(crm_ap_base + MXC_CRMAP_ADCR);
        unsigned long lfdf = (reg & MXC_CRMAP_ADCR_LFDF_MASK) >> 
                              MXC_CRMAP_ADCR_LFDF_OFFSET;
        unsigned int dfs_en = reg & MXC_CRMAP_ADCR_DFS_DIVEN; 
        unsigned int alt_pll = reg & MXC_CRMAP_ADCR_ALT_PLL;

        if ((dfs_en > 1) && (alt_pll == 0)) {
                if (lfdf == 0) {
                        lfdf = 1;
                } else if (lfdf == 1 || lfdf == 2) {
                        lfdf = 2 * lfdf;
                } else {
                        lfdf = 8;
                }
        } else {
                lfdf = 1;
        }
        return lfdf;
}

/*!
 * This function is called to gate off the individual module clocks
 *
 * @param clks     as defined in enum mxc_clocks
 */
void mxc_clks_disable(enum mxc_clocks clks) 
{
        unsigned long flags;
        volatile unsigned long reg;
        
        spin_lock_irqsave(&mxc_crm_lock, flags);

        switch(clks) {
        case UART1_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg &= ~MXC_CRMAP_APRA_UART1EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case UART2_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg &= ~MXC_CRMAP_APRA_UART2EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case UART3_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg &= ~MXC_CRMAP_APRA_UART3EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case SSI1_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg &= ~MXC_CRMAP_AMLPMRA_MLPMA4_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                reg &= ~MXC_CRMAP_ACDER1_SSI1EN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break;
        case SSI2_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg &= ~MXC_CRMAP_AMLPMRE1_MLPME3_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                reg &= ~MXC_CRMAP_ACDER1_SSI2EN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break; 
        case CSI_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                reg &= ~MXC_CRMAP_ACDER1_CSIEN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break;
        case NFC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER2);
                reg &= ~MXC_CRMAP_ACDER2_NFCEN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER2);
                break;
        case I2C_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg &= ~MXC_CRMAP_AMLPMRA_MLPMA6_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case MU_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg &= ~MXC_CRMAP_AMLPMRA_MLPMA2_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case CSPI1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE2);
                reg &= ~MXC_CRMAP_AMLPMRE2_MLPME0_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE2);
                break;
        case CSPI2_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg &= ~MXC_CRMAP_AMLPMRE1_MLPME6_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                break;
        case USB_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg &= ~MXC_CRMAP_AMLPMRE1_MLPME7_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg &= ~MXC_CRMAP_AMLPMRG_MLPMG5_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER2);
                reg &= ~MXC_CRMAP_ACDER2_USBEN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER2);
                break;
        case SDHC1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg &= ~MXC_CRMAP_AMLPMRE1_MLPME4_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_APRB);
                reg &= ~MXC_CRMAP_APRB_SDHC1EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRB);
                break;
        case SDHC2_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg &= ~MXC_CRMAP_AMLPMRE1_MLPME5_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_APRB);
                reg &= ~MXC_CRMAP_APRB_SDHC2EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRB);
                break;
        case SPBA_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC0_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case SDMA_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC2_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg &= ~MXC_CRMAP_AMLPMRG_MLPMG2_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case EDIO_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC3_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case RTC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC1_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case EPIT1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC5_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case GPT_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC4_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case PWM_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC7_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case WDOG_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRD);
                reg &= ~MXC_CRMAP_AMLPMRD_MLPMD7_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRD);
                break;
        case RNG_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRD);
                reg &= ~MXC_CRMAP_AMLPMRD_MLPMD0_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRD);
                break;
        case SCC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRD);
                reg &= ~MXC_CRMAP_AMLPMRD_MLPMD2_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRD);
                break;
        case IPU_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg &= ~MXC_CRMAP_AMLPMRC_MLPMC9_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg &= ~MXC_CRMAP_AMLPMRG_MLPMG3_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case KPP_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg &= ~MXC_CRMAP_AMLPMRA_MLPMA3_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case RTIC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg &= ~MXC_CRMAP_AMLPMRG_MLPMG0_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case GEM_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg &= ~MXC_CRMAP_AMLPMRG_MLPMG1_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case HAC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg &= ~MXC_CRMAP_AMLPMRG_MLPMG7_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case DSM_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg &= ~MXC_CRMAP_AMLPMRA_MLPMA1_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case SIM1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg &= ~MXC_CRMAP_APRA_SIMEN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case CPU_CLK:
        case IPG_CLK:
        case AHB_CLK:
                break;
        }

        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

/*!
 * This function is called to enable the individual module clocks
 *
 * @param clks     as defined in enum mxc_clocks
 */
void mxc_clks_enable(enum mxc_clocks clks)
{
        unsigned long flags;
        volatile unsigned long reg;

        spin_lock_irqsave(&mxc_crm_lock, flags);

        switch(clks) {
        case UART1_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg |= MXC_CRMAP_APRA_UART1EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case UART2_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg |= MXC_CRMAP_APRA_UART2EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case UART3_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg |= MXC_CRMAP_APRA_UART3EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case SSI1_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg |= MXC_CRMAP_AMLPMRA_MLPMA4_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                reg |= MXC_CRMAP_ACDER1_SSI1EN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break;
        case SSI2_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg |= MXC_CRMAP_AMLPMRE1_MLPME3_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                reg |= MXC_CRMAP_ACDER1_SSI2EN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break; 
        case CSI_BAUD:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER1);
                reg |= MXC_CRMAP_ACDER1_CSIEN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER1);
                break;
        case NFC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER2);
                reg |= MXC_CRMAP_ACDER2_NFCEN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER2);
                break;
        case I2C_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg |= MXC_CRMAP_AMLPMRA_MLPMA6_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case MU_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg |= MXC_CRMAP_AMLPMRA_MLPMA2_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case CSPI1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE2);
                reg |= MXC_CRMAP_AMLPMRE2_MLPME0_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE2);
                break;
        case CSPI2_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg |= MXC_CRMAP_AMLPMRE1_MLPME6_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                break;
        case USB_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg |= MXC_CRMAP_AMLPMRE1_MLPME7_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg |= MXC_CRMAP_AMLPMRG_MLPMG5_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg = readl(crm_ap_base + MXC_CRMAP_ACDER2);
                reg |= MXC_CRMAP_ACDER2_USBEN;
                writel(reg, crm_ap_base + MXC_CRMAP_ACDER2);
                break;
        case SDHC1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg |= MXC_CRMAP_AMLPMRE1_MLPME4_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_APRB);
                reg |= MXC_CRMAP_APRB_SDHC1EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRB);
                break;
        case SDHC2_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg |= MXC_CRMAP_AMLPMRE1_MLPME5_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRE1);
                reg = readl(crm_ap_base + MXC_CRMAP_APRB);
                reg |= MXC_CRMAP_APRB_SDHC2EN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRB);
                break;
        case SPBA_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC0_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case SDMA_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC2_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg |= MXC_CRMAP_AMLPMRG_MLPMG2_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case EDIO_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC3_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case RTC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC1_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case EPIT1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC5_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case GPT_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC4_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case PWM_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC7_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                break;
        case WDOG_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRD);
                reg |= MXC_CRMAP_AMLPMRD_MLPMD7_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRD);
                break;
        case RNG_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRD);
                reg |= MXC_CRMAP_AMLPMRD_MLPMD0_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRD);
                break;
        case SCC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRD);
                reg |= MXC_CRMAP_AMLPMRD_MLPMD2_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRD);
                break;
        case IPU_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg |= MXC_CRMAP_AMLPMRC_MLPMC9_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRC);
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg |= MXC_CRMAP_AMLPMRG_MLPMG3_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case KPP_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg |= MXC_CRMAP_AMLPMRA_MLPMA3_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case RTIC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg |= MXC_CRMAP_AMLPMRG_MLPMG0_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case GEM_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg |= MXC_CRMAP_AMLPMRG_MLPMG1_MASK; 
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case HAC_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRG);
                reg |= MXC_CRMAP_AMLPMRG_MLPMG7_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRG);
                break;
        case DSM_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_AMLPMRA);
                reg |= MXC_CRMAP_AMLPMRA_MLPMA1_MASK;
                writel(reg, crm_ap_base + MXC_CRMAP_AMLPMRA);
                break;
        case SIM1_CLK:
                reg = readl(crm_ap_base + MXC_CRMAP_APRA);
                reg &= ~MXC_CRMAP_APRA_SIMEN;
                writel(reg, crm_ap_base + MXC_CRMAP_APRA);
                break;
        case CPU_CLK:
        case IPG_CLK:
        case AHB_CLK:
                break;
        }
        spin_unlock_irqrestore(&mxc_crm_lock, flags);
}

EXPORT_SYMBOL(mxc_pll_set);
EXPORT_SYMBOL(mxc_pll_get);
EXPORT_SYMBOL(mxc_pll_clock);
EXPORT_SYMBOL(mxc_get_clocks);
EXPORT_SYMBOL(mxc_set_clocks_pll);
EXPORT_SYMBOL(mxc_set_clocks_div);
EXPORT_SYMBOL(mxc_get_ref_clk);
EXPORT_SYMBOL(mxc_main_clock_divider);
EXPORT_SYMBOL(mxc_peri_clock_divider);
EXPORT_SYMBOL(mxc_get_lfdf_value);
EXPORT_SYMBOL(mxc_clks_disable);
EXPORT_SYMBOL(mxc_clks_enable);
